How to save ID2D1Bitmap to PNG file - image

I faced a problem when trying to save ID2D1Bitmap (that created from ID2D1HwndRenderTarget) to PNG File. The output image is just empty with white color. HRESULT returned from function call EndDraw() is -2003238894.
Thanks for any help.
Here is my code:
HRESULT CImageUtil::SaveBitmapToFile(PCWSTR uri,ID2D1Bitmap* pBitmap,ID2D1RenderTarget* pRenderTarget)
{
HRESULT hr = S_OK;
ID2D1Factory *pD2DFactory = NULL;
IWICBitmap *pWICBitmap = NULL;
ID2D1RenderTarget *pRT = NULL;
IWICBitmapEncoder *pEncoder = NULL;
IWICBitmapFrameEncode *pFrameEncode = NULL;
IWICStream *pStream = NULL;
if (SUCCEEDED(hr))
{
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
}
//
// Create IWICBitmap and RT
//
UINT sc_bitmapWidth = pBitmap->GetSize().width;
UINT sc_bitmapHeight = pBitmap->GetSize().height;
if (SUCCEEDED(hr))
{
hr = m_pWICFactory->CreateBitmap(
sc_bitmapWidth,
sc_bitmapHeight,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnLoad,
&pWICBitmap
);
}
if (SUCCEEDED(hr))
{
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
hr = pD2DFactory->CreateWicBitmapRenderTarget(
pWICBitmap,
rtProps,
&pRT
);
}
if (SUCCEEDED(hr))
{
//
// Render into the bitmap
//
pRT->BeginDraw();
pRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
pRT->DrawBitmap(pBitmap);
pRT->EndDraw();
}
if (SUCCEEDED(hr))
{
//
// Save image to file
//
hr = m_pWICFactory->CreateStream(&pStream);
}
WICPixelFormatGUID format = GUID_WICPixelFormat32bppPBGRA;
if (SUCCEEDED(hr))
{
hr = pStream->InitializeFromFilename(uri, GENERIC_WRITE);
}
if (SUCCEEDED(hr))
{
hr = m_pWICFactory->CreateEncoder(GUID_ContainerFormatPng, NULL, &pEncoder);
}
if (SUCCEEDED(hr))
{
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr))
{
hr = pEncoder->CreateNewFrame(&pFrameEncode, NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->Initialize(NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->SetSize(sc_bitmapWidth, sc_bitmapHeight);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->SetPixelFormat(&format);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->WriteSource(pWICBitmap, NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->Commit();
}
if (SUCCEEDED(hr))
{
hr = pEncoder->Commit();
}
SafeRelease(&pD2DFactory);
SafeRelease(&pWICBitmap);
SafeRelease(&pRT);
SafeRelease(&pEncoder);
SafeRelease(&pFrameEncode);
SafeRelease(&pStream);
return hr;
}

How would you even know if you had an error, since you just swallow errors and continue instead of logging where they came from? You get a non-zero hresult, so first figure out which function it comes from by adding a printf or fprintf after every single function call. And you have a glaring omission in the block:
if (SUCCEEDED(hr))
{
//
// Render into the bitmap
//
pRT->BeginDraw();
pRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
pRT->DrawBitmap(pBitmap);
pRT->EndDraw();
}
if (SUCCEEDED(hr))
You don't bother to assign hr anywhere in there, so you wouldn't even know if any of them are erroring out. Obviously, Clear() and the final png writing work fine, since you get a good file, so it's DrawBitmap or one of the bitmap creation calls that are failing.

Related

C++ - Windows Shell API - Wrong path while iterating through a folder

Consider the following code:
bool ListFolderContent(const std::wstring& fileName)
{
PIDLIST_ABSOLUTE pidl = nullptr;
if (FAILED(::SHILCreateFromPath(fileName.c_str(), &pidl, nullptr)))
return false;
IShellFolder* pShellfolder = nullptr;
LPCITEMIDLIST pidlRelative = nullptr;
HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, &pidlRelative);
if (FAILED(hr))
{
::CoTaskMemFree(pidl);
return false;
}
IEnumIDList* pEnumIDList = nullptr;
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnumIDList);
if (FAILED(hr))
{
pShellfolder->Release();
::CoTaskMemFree(pidl);
return false;
}
while (1)
{
LPITEMIDLIST pChild = nullptr;
hr = pEnumIDList->Next(1, &pChild, nullptr);
if (FAILED(hr))
{
pShellfolder->Release();
::CoTaskMemFree(pidl);
return false;
}
if (hr == S_FALSE)
break;
wchar_t buffer[MAX_PATH + 1];
if (::SHGetPathFromIDListW(pChild, buffer))
{
::OutputDebugString(buffer);
::OutputDebugString(L"\r\n");
}
}
pShellfolder->Release();
::CoTaskMemFree(pidl);
return true;
}
This code works well and logs the content of the folder owning the given file I pass through the fileName parameter.
However I have an issues with this code: Whatever I pass as file name, the logged path is always C:\Users\Admin\Desktop, and NOT the path to the parent folder I'm iterating, as expected. On the other hand the file names are correct.
Can someone explain me what I'm doing wrong?
You are passing just the last component ("filename") here: SHGetPathFromIDListW(pChild, buffer). Since the desktop is the root you are basically asking to get the filesystem path of [Desktop] [Filename] from the namespace.
There are two solutions:
pShellfolder->GetDisplayNameOf(pChild, SHGDN_FORPARSING, ...)+StrRetToBuf. This is fast since you already have an instance of the folder interface.
Call SHGetPathFromIDList with an absolute (full) pidl. On the pidl from SHILCreateFromPath, call ILCloneFull, ILRemoveLastID and ILCombine(clone, pChild).
HRESULT Example()
{
WCHAR buf[MAX_PATH];
GetWindowsDirectory(buf, MAX_PATH);
PathAppend(buf, L"Explorer.exe");
PIDLIST_ABSOLUTE pidl, pidlFullItem;
HRESULT hr = SHILCreateFromPath(buf, &pidl, nullptr); // %windir%\Explorer.exe
if (FAILED(hr)) return hr;
LPITEMIDLIST pLeaf;
IShellFolder*pShellfolder; // %windir%
hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, nullptr);
if (SUCCEEDED(hr))
{
IEnumIDList*pEnum;
// Method 1:
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnum);
if (SUCCEEDED(hr))
{
for (; S_OK == (hr = pEnum->Next(1, &pLeaf, nullptr));)
{
STRRET sr;
hr = pShellfolder->GetDisplayNameOf(pLeaf, SHGDN_FORPARSING, &sr);
if (SUCCEEDED(hr))
{
hr = StrRetToBuf(&sr, pLeaf, buf, MAX_PATH);
if (SUCCEEDED(hr)) wprintf(L"M1: Item: %s\n", buf);
}
}
pEnum->Release();
}
// Method 2:
ILRemoveLastID(pidl); // %windir%\Explorer.exe => %windir%
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnum);
if (SUCCEEDED(hr))
{
for (; S_OK == (hr = pEnum->Next(1, &pLeaf, nullptr));)
{
pidlFullItem = ILCombine(pidl, pLeaf); // %windir% + Filename
if (pidlFullItem)
{
hr = SHGetPathFromIDListW(pidlFullItem, buf);
if (SUCCEEDED(hr)) wprintf(L"M2: Item: %s\n", buf);
ILFree(pidlFullItem);
}
}
pEnum->Release();
}
pShellfolder->Release();
}
ILFree(pidl);
return hr;
}

A disc's recursive path : Time to long C++

I want to build a class similar to "CFindDialog" with MFC Windows with color. I route the disk with a recursive function that uses "FindFirstFile". But when I switch to settings the system disk (C: . . .). The time is to long several seconds and so I'm on an SDD.
I have two questions:
Are there any low-level functions that allow you to go faster?
How does DLL comdlg32 go so fast?
Jean Bezet
I cannot find the "CFindDialog" class you mentioned, but you could try Windows Search, First is to index the drive you want to search(Only once):
Control Panel> Index Option> Modify, check the dirves.
There are some code samples in the document, and I also provide a sample without UI here. I made some simple modifications to this sample to specify the search content:
#include <windows.h>
#include <searchapi.h>
#include <iostream>
#include <atldbcli.h>
using namespace std;
class CMyAccessor
{
public:
WCHAR _szItemUrl[2048];
__int64 _size;
BEGIN_COLUMN_MAP(CMyAccessor)
COLUMN_ENTRY(1, _szItemUrl)
COLUMN_ENTRY(2, _size)
END_COLUMN_MAP()
};
HRESULT GetSQLStringFromParams(LCID lcidContentLocaleParam,
PCWSTR pszContentPropertiesParam,
LCID lcidKeywordLocaleParam,
LONG nMaxResultsParam,
PCWSTR pszSelectColumnsParam,
PCWSTR pszSortingParam,
SEARCH_QUERY_SYNTAX sqsSyntaxParam,
SEARCH_TERM_EXPANSION steTermExpansionParam,
PCWSTR pszWhereRestrictionsParam,
PCWSTR pszExprParam,
PWSTR* ppszSQL)
{
ISearchQueryHelper* pQueryHelper;
// Create an instance of the search manager
ISearchManager* pSearchManager;
HRESULT hr = CoCreateInstance(__uuidof(CSearchManager), NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&pSearchManager));
if (SUCCEEDED(hr))
{
// Get the catalog manager from the search manager
ISearchCatalogManager* pSearchCatalogManager;
hr = pSearchManager->GetCatalog(L"SystemIndex", &pSearchCatalogManager);
if (SUCCEEDED(hr))
{
// Get the query helper from the catalog manager
hr = pSearchCatalogManager->GetQueryHelper(&pQueryHelper);
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryContentLocale(lcidContentLocaleParam);
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryContentProperties(pszContentPropertiesParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryKeywordLocale(lcidKeywordLocaleParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryMaxResults(nMaxResultsParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QuerySelectColumns(pszSelectColumnsParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QuerySorting(pszSortingParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QuerySyntax(sqsSyntaxParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryTermExpansion(steTermExpansionParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryWhereRestrictions(pszWhereRestrictionsParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->GenerateSQLFromUserQuery(pszExprParam, ppszSQL);
}
pQueryHelper->Release();
}
pSearchCatalogManager->Release();
}
pSearchManager->Release();
}
return hr;
}
void WindowsSearch(PCWSTR szPath, PCWSTR szFileName)
{
PWSTR pszSQL;
wstring script = L"AND SCOPE ='file:///";
script += szPath;
script += L"'";
wstring filename = L"FileName:";
filename += szFileName;
HRESULT hr = GetSQLStringFromParams(1033, L"", 1033, -1, L"System.ItemPathDisplay, System.Size",
L"", SEARCH_ADVANCED_QUERY_SYNTAX, SEARCH_TERM_NO_EXPANSION, script.c_str(),
filename.c_str(), &pszSQL);
if (SUCCEEDED(hr))
{
wcout << L"Generated query: " << pszSQL << endl;
CDataSource cDataSource;
hr = cDataSource.OpenFromInitializationString(L"provider=Search.CollatorDSO.1;EXTENDED PROPERTIES='Application=Windows'");
if (SUCCEEDED(hr))
{
CSession cSession;
hr = cSession.Open(cDataSource);
if (SUCCEEDED(hr))
{
// cCommand is derived from CMyAccessor which has binding information in column map
// This allows ATL to put data directly into apropriate class members.
CCommand<CAccessor<CMyAccessor>, CRowset> cCommand;
hr = cCommand.Open(cSession, pszSQL);
if (SUCCEEDED(hr))
{
__int64 maxValue = 0;
__int64 minValue = ULONG_MAX;
for (hr = cCommand.MoveFirst(); S_OK == hr; hr = cCommand.MoveNext())
{
wcout << cCommand._szItemUrl << L": " << cCommand._size << L" bytes" << endl;
maxValue = max(maxValue, cCommand._size);
minValue = min(minValue, cCommand._size);
}
wcout << L"Max:" << maxValue << L"Min:" << minValue << endl;
cCommand.Close();
}
cCommand.ReleaseCommand();
}
}
CoTaskMemFree(pszSQL);
}
}
int main()
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
WindowsSearch(L"C:\\", L"test*.txt");
CoUninitialize();
}
}
Thank you for your response. That doesn't quite address my problem. My function is recursive it broswse a hard drive. The travel time is too important when I throw it on c: (approximately 660,000 files). I memorize this description in a string array. If I remove this memorization it does not change the real time. Here's my function:
<<
bool Parcours_disque::EnumerateFolder(LPCWSTR lpcszFolder, DWORD nLevel)
{
// LPWIN32_FIND_DATAA fdd = __nullptr;
WIN32_FIND_DATAW fdd;
HANDLE hFind = INVALID_HANDLE_VALUE;
hFind = FindFirstFileW((lpcszFolder),(LPWIN32_FIND_DATAW) &fdd); // (LPCWSTR)
if (INVALID_HANDLE_VALUE == hFind)
{
return false;
}
do
{
if (fdd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY))
{
if (_T('.') != fdd.cFileName[0])
{
if (_T('$') != fdd.cFileName[0])
{
// Store in buffer
.....
EnumerateFolder(((CString)lpcszFolder).SpanExcluding(_T("*")) + (CString)fdd.cFileName + _T("\\*"), nLevel + 1); // Here's the recursive call!
}
}
}
else
{
// Store in buffer
.....
}
} while (FindNextFileW(hFind, (LPWIN32_FIND_DATAW) &fdd) != 0);
FindClose(hFind);
return true;
}
Microsoft does the same with CFileDialog object (MFC) and when you go through the disk it's immediate how does it do it? hidden functions?
Thank you

Windows Custom Credentials Provider using MSV1_0 fails with INVALID_PARAMETER

I am trying to implement a local login from my custom Credentials Provider. For that, I try to use the MSV1_0 authentication package, but it keeps failing, yielding an INVALID_PARAMETER status.
The code seems like that:
static void _UnicodeStringPackedUnicodeStringCopy(
const UNICODE_STRING& rus,
PWSTR pwzBuffer,
UNICODE_STRING* pus
) {
pus->Length = rus.Length;
pus->MaximumLength = rus.Length;
pus->Buffer = pwzBuffer;
CopyMemory(pus->Buffer, rus.Buffer, pus->Length);
}
HRESULT LsaInitStringW(PUNICODE_STRING pszDestinationString, PCWSTR pszSourceString)
{
size_t cchLength;
HRESULT hr = StringCchLengthW(pszSourceString, USHORT_MAX, &cchLength);
if (SUCCEEDED(hr))
{
USHORT usLength;
hr = SizeTToUShort(cchLength, &usLength);
if (SUCCEEDED(hr))
{
pszDestinationString->Buffer = (PWCHAR)pszSourceString;
pszDestinationString->Length = usLength * sizeof(WCHAR);
pszDestinationString->MaximumLength = pszDestinationString->Length + 1;
hr = S_OK;
}
}
return hr;
}
HRESULT MsvLogonPack(
const MSV1_0_INTERACTIVE_LOGON& milIn,
BYTE** prgb,
DWORD* pcb
) {
size_t cb = sizeof(milIn)
+ milIn.LogonDomainName.Length
+ milIn.UserName.Length
+ milIn.Password.Length;
MSV1_0_INTERACTIVE_LOGON* milOut = (MSV1_0_INTERACTIVE_LOGON*)CoTaskMemAlloc(cb);
if (!milOut) {
return E_OUTOFMEMORY;
}
milOut->MessageType = milIn.MessageType;
BYTE *pbBuffer = (BYTE*)milOut + sizeof(*milOut);
_UnicodeStringPackedUnicodeStringCopy(milIn.LogonDomainName, (PWSTR)pbBuffer, &milOut->LogonDomainName);
pbBuffer += milOut->LogonDomainName.Length;
_UnicodeStringPackedUnicodeStringCopy(milIn.UserName, (PWSTR)pbBuffer, &milOut->UserName);
pbBuffer += milOut->UserName.Length;
_UnicodeStringPackedUnicodeStringCopy(milIn.Password, (PWSTR)pbBuffer, &milOut->Password);
pbBuffer += milOut->Password.Length;
if (pbBuffer != (BYTE*)milOut + cb) {
return E_ABORT;
}
*prgb = (BYTE*)milOut;
*pcb = cb;
return S_OK;
}
HRESULT GetMsvPackage(ULONG * pulAuthPackage) {
HRESULT hr;
HANDLE hLsa;
NTSTATUS status = LsaConnectUntrusted(&hLsa);
if (SUCCEEDED(HRESULT_FROM_NT(status))) {
ULONG ulAuthPackage;
LSA_STRING lsaszKerberosName;
LsaInitString(&lsaszKerberosName, MSV1_0_PACKAGE_NAME);
status = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName, &ulAuthPackage);
if (SUCCEEDED(HRESULT_FROM_NT(status))) {
*pulAuthPackage = ulAuthPackage;
hr = S_OK;
}
else {
hr = HRESULT_FROM_NT(status);
}
LsaDeregisterLogonProcess(hLsa);
}
else {
hr = HRESULT_FROM_NT(status);
}
return hr;
}
HRESULT MyCredential::CompleteAuthentication(CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs,
PWSTR* ppwszOptionalStatusText,
CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon) {
HRESULT hr;
pcpcs->clsidCredentialProvider = CLSID_MyProvider;
MSV1_0_INTERACTIVE_LOGON mil;
mil.MessageType = MsV1_0WorkstationUnlockLogon;
hr = LsaInitStringW(&mil.LogonDomainName, L"");
if (SUCCEEDED(hr)) hr = LsaInitStringW(&mil.UserName, L"tester");
if (SUCCEEDED(hr)) hr = LsaInitStringW(&mil.Password, L"12345");
if (SUCCEEDED(hr)) {
hr = MsvLogonPack(mil, &pcpcs->rgbSerialization, &pcpcs->cbSerialization);
if (SUCCEEDED(hr)) {
ULONG ulAuthPackage;
hr = GetMsvPackage(&ulAuthPackage);
if (SUCCEEDED(hr)) {
pcpcs->ulAuthenticationPackage = ulAuthPackage;
}
}
}
return hr;
}
This keeps giving a status of INVALID_PARAMETER, with sub-status 0. I tried replacing the MsV1_0InteractiveLogon with MsV1_0WorkstationUnlockLogon, which got me a status of STATUS_LOGON_FAILURE with sub-status INTERNAL_ERROR.
What would be suggested to solve this issue?
After some research and trial, I figured out the problem. The issue was in the Unicode Strings being absolute, while they are required to be relative to the start of the structure. So I made them relative:
_UnicodeStringPackedUnicodeStringCopy(milIn.LogonDomainName, (PWSTR)pbBuffer, &milOut->LogonDomainName);
milOut->LogonDomainName.Buffer = (PWSTR)(pbBuffer - (BYTE*)milOut);
pbBuffer += milOut->LogonDomainName.Length;
_UnicodeStringPackedUnicodeStringCopy(milIn.UserName, (PWSTR)pbBuffer, &milOut->UserName);
milOut->UserName.Buffer = (PWSTR)(pbBuffer - (BYTE*)milOut);
pbBuffer += milOut->UserName.Length;
_UnicodeStringPackedUnicodeStringCopy(milIn.Password, (PWSTR)pbBuffer, &milOut->Password);
milOut->Password.Buffer = (PWSTR)(pbBuffer - (BYTE*)milOut);
pbBuffer += milOut->Password.Length;
This behavior is documented for KERB_CERTIFICATE_LOGON structure, but not for MSV1_0_INTERACTIVE_LOGON, for some reason.

Getting GUID of group with IADs interface

I'm using the following C++ code to get all groups for a user:
(from https://msdn.microsoft.com/en-us/library/windows/desktop/aa746342(v=vs.85).aspx. I just added a few liens to get the group GUID as well)
HRESULT CheckUserGroups(IADsUser *pUser)
{
IADsMembers *pGroups;
HRESULT hr = S_OK;
hr = pUser->Groups(&pGroups);
pUser->Release();
if (FAILED(hr)) return hr;
IUnknown *pUnk;
hr = pGroups->get__NewEnum(&pUnk);
if (FAILED(hr)) return hr;
pGroups->Release();
IEnumVARIANT *pEnum;
hr = pUnk->QueryInterface(IID_IEnumVARIANT, (void**)&pEnum);
if (FAILED(hr)) return hr;
pUnk->Release();
// Enumerate.
BSTR bstr;
VARIANT var;
IADs *pADs;
ULONG lFetch;
IDispatch *pDisp;
VariantInit(&var);
hr = pEnum->Next(1, &var, &lFetch);
while (hr == S_OK)
{
if (lFetch == 1)
{
pDisp = V_DISPATCH(&var);
pDisp->QueryInterface(IID_IADs, (void**)&pADs);
pADs->get_Name(&bstr);
printf("Group belonged: %S\n", bstr);
SysFreeString(bstr);
pADs->get_GUID(&bstr);
printf("Group guid: %S\n", bstr);
SysFreeString(bstr);
pADs->Release();
}
VariantClear(&var);
pDisp = NULL;
hr = pEnum->Next(1, &var, &lFetch);
};
hr = pEnum->Release();
return S_OK;
}
int main()
{
CoInitialize(NULL);
CComPtr<IADsUser> pUser;
std::wstring ADpathName = std::wstring(L"WinNT://domain/user");
if( ADsGetObject(ADpathName.c_str(), IID_IADsUser, (void**)&pUser) == S_OK )
{
CheckUserGroups(pUser);
}
return 0;
}
However, the GUID for all groups is the same and doesn't reflect the real group GUID. why is that?

SHAutoComplete & current working directory?

I am using SHAutoComplete with the SHACF_FILESYSTEM option. The problem is, files relative to the current working directory are not autocompleted. There are no suggestions for relative paths -- for example, the working directory contains settings.txt, but I can type "settings" into the edit and nothing appears.
Is there a relatively easy solution? Or do I have to override the autocomplete behaviour with some own lookup?
Thank you in advance!
Please take a look at the documentation at:
http://msdn.microsoft.com/en-us/library/bb776884
You need to explicit specify the "Current directory" as an option. This must be done throu IACList2::SetOptions http://msdn.microsoft.com/en-us/library/windows/desktop/bb776376
==> You must use the COM interface, to set the options you want... Here is an example:
HRESULT EnableAutoComplete(HWND hWndEdit, LPWSTR szCurrentWorkingDirectory = NULL, AUTOCOMPLETELISTOPTIONS acloOptions = ACLO_NONE, AUTOCOMPLETEOPTIONS acoOptions = ACO_AUTOSUGGEST, REFCLSID clsid = CLSID_ACListISF)
{
IAutoComplete *pac;
HRESULT hr = CoCreateInstance(CLSID_AutoComplete,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pac));
if (FAILED(hr))
{
return hr;
}
IUnknown *punkSource;
hr = CoCreateInstance(clsid,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&punkSource));
if (FAILED(hr))
{
pac->Release();
return hr;
}
if ( (acloOptions != ACLO_NONE) || (szCurrentWorkingDirectory != NULL) )
{
IACList2 *pal2;
hr = punkSource->QueryInterface(IID_PPV_ARGS(&pal2));
if (SUCCEEDED(hr))
{
if (acloOptions != ACLO_NONE)
{
hr = pal2->SetOptions(acloOptions);
}
if (szCurrentWorkingDirectory != NULL)
{
ICurrentWorkingDirectory *pcwd;
hr = pal2->QueryInterface(IID_PPV_ARGS(&pcwd));
if (SUCCEEDED(hr))
{
hr = pcwd->SetDirectory(szCurrentWorkingDirectory);
pcwd->Release();
}
}
pal2->Release();
}
}
hr = pac->Init(hWndEdit, punkSource, NULL, NULL);
if (acoOptions != ACO_NONE)
{
IAutoComplete2 *pac2;
hr = pac->QueryInterface(IID_PPV_ARGS(&pac2));
if (SUCCEEDED(hr))
{
hr = pac2->SetOptions(acoOptions);
pac2->Release();
}
}
punkSource->Release();
pac->Release();
}
you can call it via:
wchar_t szCurDir[MAX_PATH];
GetCurrentDirectoryW(MAX_PATH, szCurDir);
EnableAutoComplete(m_txtBox1.m_hWnd, szCurDir, ACLO_CURRENTDIR);

Resources