Error with function call CRegKey::QueryStringValue - c++

Today i found this strange error in querying the time zone information .
The code to read the display name of time zones looks like this
typedef struct {
LONG Bias;
LONG StandardBias;
LONG DaylightBias;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
} TZI, * PTZI;
CRegKey RegKey;
CString regKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
int idx = 0;
bool bMoreKeys = true;
bool bSuccess = true;
while (bMoreKeys && bSuccess) {
CString sSubkeyName;
DWORD nLength = 200;
LPTSTR sBuffer = sSubkeyName.GetBufferSetLength(nLength);
FILETIME ftLastWriteTime;
bMoreKeys = (RegKey.EnumKey(idx, sBuffer, & nLength, & ftLastWriteTime) == ERROR_SUCCESS);
sSubkeyName.ReleaseBuffer();
if (!bMoreKeys) {
bMoreKeys = false;
break;
}
CString sSubKeyPath = regKey + _T("\\") + sSubkeyName;
CRegKey subKey;
if (subKey.Open(HKEY_LOCAL_MACHINE, sSubKeyPath, KEY_READ) != ERROR_SUCCESS) {
//LOG_ERROR
bSuccess = false;
break;
}
// Get the display name
CString sDispName;
DWORD nDispBufferLength = 1000;
LPTSTR sDispBuffer = sDispName.GetBufferSetLength(nDispBufferLength);
if (subKey.QueryStringValue(_T("Display"), sDispBuffer, & nDispBufferLength) != ERROR_SUCCESS) {
//LOG_ERROR
bSuccess = false;
break;
}
sDispName.ReleaseBuffer();
// Get the Bias (for later sorting);
TZI tzi;
nLength = sizeof(tzi);
if (subKey.QueryBinaryValue(_T("TZI"), & tzi, & nLength) != ERROR_SUCCESS && nLength != sizeof(tzi)) {
//LOG_ERROR
bSuccess = false;
}
(void) subKey.Close();
idx++;
}
But for some of the time zones for eg : argentina the return value is not error_success.
On further debugging into the QueryStringValue , i found this
if ((nBytes % sizeof(TCHAR) != 0) || (pszValue[nBytes / sizeof(TCHAR) - 1] != 0)) {
return ERROR_INVALID_DATA;
}
and when the nBytes size is 48 for any time zone display value then always error_invalid_data is returned .
To confirm this i have changed the regkey api call to
DWORD dwType = 0;
ULONG nBytes = 256 * sizeof(TCHAR);
TCHAR displayValue[256];
if (subKey.QueryValue(
_T("Display"), & dwType, (LPBYTE) displayValue, & nBytes) == 0 && dwType == REG_SZ) {}
and i dont get the error anymore and everything is working fine .
Couldnt find out any valid reason that why it occurs.Anyone has better explanation why for all the timezones with size 48 we get the invalid data error .
Thanks in advance.
EDIT:
PS: In some machines everything is working well with the code mentioned above (i.e the first one) and in some other machines its not which is what really strange and with the second code everywhere i could see the code is working fine.

The code
CRegKey Key;
LONG nA = Key.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\Argentina Standard Time"), KEY_READ);
TCHAR pszValue[24];
ULONG nValueLength = _countof(pszValue);
LONG nB = Key.QueryStringValue(_T("Display"), pszValue, &nValueLength);
gets you a different error, 234, which is ERROR_MORE_DATA "More data is available."
This happens because the value itself does not fit fully into the provided buffer.
This is what you want instead:
CRegKey Key;
LONG nA = Key.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\Argentina Standard Time"), KEY_READ);
ULONG nValueLength = 0;
LONG nB = Key.QueryStringValue(_T("Display"), NULL, &nValueLength);
CString sValue;
if(nValueLength > 0)
{
LONG nC = Key.QueryStringValue(_T("Display"), sValue.GetBufferSetLength(nValueLength - 1), &nValueLength);
}
Or, you need to provide large enough buffer, which you assume to be sufficient.

The string in the reg may not be null-terminated. Hence the following validation fails:
if ((nBytes % sizeof(TCHAR) != 0) || (pszValue[nBytes / sizeof(TCHAR) -1] != 0))
{
return ERROR_INVALID_DATA;
}
Note that it seems to be a bug in CRegKey::QueryStringValue, RegQueryValueEx allows to read not-null-terminated strings, and it is callers responsibility to handle this case. I had come across this exact issue in the code and had to replace the call to CRegKey::QueryStringValue with my own function.

Related

C++ How To Convert String YYYYMMDD To TimeStamp

I have a program that gets installed on my laptop to a json file.
I retrieve the date of the installed program from registry and I would like to convert that date to a timestamp.
This is my code :
HKEY hUninstKey = NULL;
HKEY hAppKey = NULL;
WCHAR InstallDate[1024];
WCHAR *sRoot = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
long lResult = ERROR_SUCCESS;
DWORD dwType = KEY_ALL_ACCESS;
DWORD dwBufferSize = 0;
//Open the "Uninstall" key.
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, sRoot, 0, KEY_READ, &hUninstKey) != ERROR_SUCCESS)
{
}
for (DWORD dwIndex = 0; lResult == ERROR_SUCCESS; dwIndex++)
{
//Enumerate all sub keys...
dwBufferSize = sizeof(sAppKeyName);
if ((lResult = RegEnumKeyEx(hUninstKey, dwIndex, sAppKeyName,
&dwBufferSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS)
{
//Open the sub key.
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, sSubKey, 0, KEY_READ, &hAppKey) != ERROR_SUCCESS)
{
RegCloseKey(hAppKey);
RegCloseKey(hUninstKey);
}
//Get the display name value from the application's sub key.
dwBufferSize = sizeof((RegQueryValueEx(hAppKey, L"InstallDate", NULL, &dwType, (unsigned char*)InstallDate, &dwBufferSize) == ERROR_SUCCESS))
{
// Display Date Of Installed Program
const TCHAR* xInstallDate = (TCHAR*)InstallDate;
char DateInstall[MAX_LENGTH + 200];
wcstombs_s(&nNumCharConverted, DateInstall, MAX_LENGTH + 200,
xInstallDate, MAX_LENGTH + 200);
file << "\"',date='" << DateInstall << endl;
}
else {
//Display name value doe not exist, this application was probably uninstalled.
}
RegCloseKey(hAppKey);
}
}
RegCloseKey(hUninstKey);
Could anyone please help with my code?
You should look into the usage of std::chrono::parse. It converts string streams into chrono objects which represent time.
This is the documentation of std::chrono::parse: https://en.cppreference.com/w/cpp/chrono/parse
This is a relevant question on SO (you are possibly a duplicate): How to parse a date string into a c++11 std::chrono time_point or similar?

How to validate credentials with CredUIPromptForWindowsCredentials

I don't know why I can't unpack the authentification buffer used in CredUIPromptForWindowsCredentials with CredUnPackAuthenticationBufferW, I always get ERROR_INSUFFICIENT_BUFFER error.
I will appreciate your help.
std::wstring caption = L"Caption";
std::wstring msg= L"Msg";
CREDUI_INFOW credui = {};
credui.cbSize = sizeof(credui);
credui.hwndParent = nullptr;
credui.pszMessageText = msg.c_str();
credui.pszCaptionText = caption.c_str();
credui.hbmBanner = nullptr;
ULONG authPackage = 0;
LPVOID outCredBuffer = nullptr;
ULONG outCredSize = 0;
BOOL save = false;
LPWSTR pszUserName = nullptr;
DWORD pcchlMaxUserName = 0;
LPWSTR pszDomainName = nullptr;
DWORD pcchMaxDomainName = 0;
LPWSTR pszPassword = nullptr;
DWORD pcchMaxPassword = 0;
DWORD result = CredUIPromptForWindowsCredentialsW(&credui,
0,
&authPackage,
nullptr,
0,
&outCredBuffer,
&outCredSize,
&save,
CREDUIWIN_ENUMERATE_ADMINS);
std::cout <<CredUnPackAuthenticationBufferW(CRED_PACK_PROTECTED_CREDENTIALS
,outCredBuffer
,outCredSize
,pszUserName
,&pcchlMaxUserName
,pszDomainName
,&pcchMaxDomainName
,pszPassword
,&pcchMaxPassword) << std::endl;
std::cout << GetLastError() << std::endl; // out put 122 == ERROR_INSUFFICIENT_BUFFER
this is typical winapi pattern - api must return some information in the memory buffer. but instead allocate buffer yourself - it obligate caller to allocate buffer.
so caller must allocate buffer itself and pass it pointer and size to api.
api check buffer size - if it large enough fill information to buffer, otherwise return ERROR_INSUFFICIENT_BUFFER (assume that no another errors) or sometime ERROR_MORE_DATA. which which concrete error reurned ERROR_INSUFFICIENT_BUFFER or ERROR_MORE_DATA usual direct documented for api call. different between this 2 errors: ERROR_INSUFFICIENT_BUFFER - mean no any info filled to buffer at all, when ERROR_MORE_DATA mean some data is returned, but incomplete.
and api return to user, via some out parameter, required buffer size in this case. frequently this is done via the same inout parameter - pointer to DWORD. in input specifies the size of user allocated buffer, in output - specifies the required size of buffer or size of returned data
frequently which buffer size is required - unknown at begin. so we need or call api with 0 size buffers(s) first, or allocate some, supposedly sufficient buffer size. if buffer will be insuffient - reallocate or extend it and call api again. for some api (like CredUnPackAuthenticationBufferW) the required output buffer does not change with time (if input parameters not changed), but usual output buffer size may change between calls - even second call with buffer size returned by first call can fail with buffer size error (because returned data may grow between calls). in this case need call api in do/while(error == ERROR_INSUFFICIENT_BUFFER/ERROR_MORE_DATA) loop. but even in case output buffer does not change with time we can better do this is loop with single api call inside, instead 2 api calls.
for concrete case code can look like
ULONG cred()
{
CREDUI_INFO ci = { sizeof(ci) };
BOOL bSave = FALSE;
PVOID pvOutAuthBuffer;
ULONG ulOutAuthBufferSize;
ULONG ulAuthPackage = 0;
ULONG dwError = CredUIPromptForWindowsCredentials(
&ci, NOERROR, &ulAuthPackage, 0, 0,
&pvOutAuthBuffer, &ulOutAuthBufferSize,
&bSave, CREDUIWIN_ENUMERATE_ADMINS );
if (dwError == NOERROR)
{
ULONG cchUserName = 0;
ULONG cchPassword = 0;
ULONG cchDomain = 0;
static volatile UCHAR guz = 0;
PWSTR stack = (PWSTR)alloca(guz);
PWSTR szUserName = 0, szPassword = 0, szDomainName = 0;
ULONG cchNeed, cchAllocated = 0;
do
{
if (cchAllocated < (cchNeed = cchUserName + cchPassword + cchDomain))
{
szUserName = (PWSTR)alloca((cchNeed - cchAllocated) * sizeof(WCHAR));
cchAllocated = (ULONG)(stack - szUserName);
szPassword = szUserName + cchUserName;
szDomainName = szPassword + cchPassword;
}
dwError = CredUnPackAuthenticationBuffer(
CRED_PACK_PROTECTED_CREDENTIALS,
pvOutAuthBuffer, ulOutAuthBufferSize,
szUserName, &cchUserName,
szDomainName, &cchDomain,
szPassword, &cchPassword)
? NOERROR : GetLastError();
if (dwError == NOERROR)
{
DbgPrint("%S#%S %S\n", szDomainName, szUserName, szPassword);
break;
}
} while (dwError == ERROR_INSUFFICIENT_BUFFER);
CoTaskMemFree(pvOutAuthBuffer);
}
return dwError;
}
#RbMm - you're right! I tested it with LogonUser, and it works perfectly. Thanks.
And for a ready solution, I got this :
bool Authenticate_ADMIN_User(std::wstring caption, std::wstring msg, int maxReAsks = 0)
{
CREDUI_INFOW credui = {};
credui.cbSize = sizeof(credui);
credui.hwndParent = nullptr;
credui.pszMessageText = msg.c_str();
credui.pszCaptionText = caption.c_str();
credui.hbmBanner = nullptr;
ULONG authPackage = 0,
outCredSize = 0;
LPVOID outCredBuffer = nullptr;
BOOL save = false;
DWORD err = 0;
int tries = 0;
bool reAsk = false;
do
{
tries++;
if(CredUIPromptForWindowsCredentialsW(&credui,
err,
&authPackage,
nullptr,
0,
&outCredBuffer,
&outCredSize,
&save,
CREDUIWIN_ENUMERATE_ADMINS)
!= ERROR_SUCCESS)
return false;
ULONG cchUserName = 0;
ULONG cchPassword = 0;
ULONG cchDomain = 0;
ULONG cchNeed, cchAllocated = 0;
static volatile UCHAR guz = 0;
PWSTR stack = (PWSTR)alloca(guz);
PWSTR szUserName = nullptr, szPassword = nullptr, szDomainName = nullptr;
BOOL ret;
do{
if (cchAllocated < (cchNeed = cchUserName + cchPassword + cchDomain))
{
szUserName = (PWSTR)alloca((cchNeed - cchAllocated) * sizeof(WCHAR));
cchAllocated = (ULONG)(stack - szUserName);
szPassword = szUserName + cchUserName;
szDomainName = szPassword + cchPassword;
}
ret = CredUnPackAuthenticationBuffer(
CRED_PACK_PROTECTED_CREDENTIALS , outCredBuffer, outCredSize, szUserName, &cchUserName,
szDomainName, &cchDomain, szPassword,
&cchPassword);
}while(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
SecureZeroMemory(outCredBuffer, outCredSize);
CoTaskMemFree(outCredBuffer);
HANDLE handle = nullptr;
if (LogonUser(szUserName,
szDomainName,
szPassword,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
&handle))
{
CloseHandle(handle);
return true;
}
else
{
err = ERROR_LOGON_FAILURE;
reAsk = true;
}
}while(reAsk && tries < maxReAsks);
return false;
}

Why with a custom port monitor do I receive "The data is invalid" after a call to EnumPorts?

I am implementing a custom port monitor based off of the LocalMon sample, but when I return from my implementation of LcmEnumPorts, I receive the error "The data is invalid", and the list of ports installed on my machine is empty. Removing the monitor eliminates the error, and all ports reappear.
Why? I've checked that the structure I am returning is coherent and fits within the buffer allocated.
Example implementation of LcmEnumPorts:
_Success_(return != FALSE)
BOOL WINAPI LcmEnumPorts(
_In_ HANDLE hMonitor,
_In_opt_ LPWSTR pName,
DWORD Level,
_Out_writes_bytes_opt_(cbBuf)
LPBYTE pPorts,
DWORD cbBuf,
_Out_ LPDWORD pcbNeeded,
_Out_ LPDWORD pcReturned
)
{
UNREFERENCED_PARAMETER(pName);
UNREFERENCED_PARAMETER(hMonitor);
if (!pcbNeeded || !pcReturned || (!pPorts && (cbBuf > 0)))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
else if ((1 != Level) && (2 != Level))
{
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
const wchar_t szMonitorName[] = L"WDK Sample Port";
const wchar_t szPortName[] = L"NUL:";
size_t cbPortName = wcslen(szPortName) * sizeof(wchar_t) + sizeof(wchar_t);
size_t cbPortDesc = wcslen(szMonitorName) * sizeof(wchar_t) + sizeof(wchar_t);
size_t cbText = cbPortName + cbPortDesc;
size_t cbStruct = Level == 1 ? sizeof(PORT_INFO_1) : sizeof(PORT_INFO_2);
*pcbNeeded = (DWORD)(cbText + cbStruct);
*pcReturned = 0;
if (*pcbNeeded > cbBuf)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (Level == 1)
{
PPORT_INFO_1 pPort1 = (PPORT_INFO_1)pPorts;
LPWSTR pPortName = (LPWSTR)(pPorts + cbStruct);
StringCbCopy(pPortName, cbPortName, szPortName);
pPort1->pName = pPortName;
}
else if (Level == 2)
{
PPORT_INFO_2 pPort2 = (PPORT_INFO_2)pPorts;
LPWSTR pPortName = (LPWSTR)(pPorts + cbStruct);
StringCbCopy(pPortName, cbPortName, szPortName);
pPort2->pPortName = pPortName;
LPWSTR pPortDesc = (LPWSTR)(pPorts + cbStruct + cbPortName);
StringCbCopy(pPortDesc, cbPortDesc, szMonitorName);
pPort2->pMonitorName = pPortDesc;
pPort2->pDescription = pPortDesc;
pPort2->fPortType = PORT_TYPE_READ | PORT_TYPE_WRITE;
pPort2->Reserved = 0;
}
*pcReturned = 1;
return TRUE;
}
Though it does not appear to be documented anywhere, the spooler seems to reuse the same buffer for multiple driver calls.
The docs say:
The EnumPorts function should fill the buffer pointed to by pPort with an array of PORT_INFO_1 or PORT_INFO_2 structures. Then starting in a memory location following the last array element, the function must load all the strings pointed to by the array's structure members. Refer to localmon.dll, a sample port monitor, for an example of how to do this. The function must also return the number of structures supplied (that is, the number of supported ports) by placing the number in the location pointed to by pcReturned.
But in reality, it seems that the same buffer is reused for all of the print monitors. Each time, the top pointer is moved down for each PORT_INFO_1/2 structure, and the bottom pointer is moved up for each allocated string.
Reviewing localmon shows it exclusively allocates strings from the bottom of the buffer. Changing the sample to do the same allows it to execute without error.
_Success_(return != FALSE)
BOOL
LcmEnumPorts(
_In_ HANDLE hMonitor,
_In_opt_ LPWSTR pName,
DWORD Level,
_Out_writes_bytes_opt_(cbBuf)
LPBYTE pPorts,
DWORD cbBuf,
_Out_ LPDWORD pcbNeeded,
_Out_ LPDWORD pcReturned
)
{
UNREFERENCED_PARAMETER(pName);
UNREFERENCED_PARAMETER(hMonitor);
if (!pcbNeeded || !pcReturned || (!pPorts && (cbBuf > 0)))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
else if ((1 != Level) && (2 != Level))
{
SetLastError(ERROR_INVALID_LEVEL);
return FALSE;
}
const wchar_t szMonitorName[] = L"WDK Sample Port";
const wchar_t szPortName[] = L"NUL:";
size_t cbPortName = wcslen(szPortName) * sizeof(wchar_t) + sizeof(wchar_t);
size_t cbPortDesc = wcslen(szMonitorName) * sizeof(wchar_t) + sizeof(wchar_t);
size_t cbText = cbPortName + cbPortDesc;
size_t cbStruct = Level == 1 ? sizeof(PORT_INFO_1) : sizeof(PORT_INFO_2);
*pcbNeeded = (DWORD)(cbText + cbStruct);
*pcReturned = 0;
if (*pcbNeeded > cbBuf)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
if (Level == 1)
{
PPORT_INFO_1 pPort1 = (PPORT_INFO_1)pPorts;
// Changed vvvvvvvvvvvvvvvvvvvvvvvvvvv
LPWSTR pPortName = (LPWSTR)(pPorts + cbBuf - cbPortName);
StringCbCopy(pPortName, cbPortName, szPortName);
pPort1->pName = pPortName;
}
else if (Level == 2)
{
PPORT_INFO_2 pPort2 = (PPORT_INFO_2)pPorts;
// Changed vvvvvvvvvvvvvvvvvvvvvvvvvvv
LPWSTR pPortName = (LPWSTR)(pPorts + cbBuf - cbPortName);
StringCbCopy(pPortName, cbPortName, szPortName);
pPort2->pPortName = pPortName;
// Changed vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
LPWSTR pPortDesc = (LPWSTR)(pPorts + cbBuf - cbPortName - cbPortDesc);
StringCbCopy(pPortDesc, cbPortDesc, szMonitorName);
pPort2->pMonitorName = pPortDesc;
pPort2->pDescription = pPortDesc;
pPort2->fPortType = PORT_TYPE_READ | PORT_TYPE_WRITE;
pPort2->Reserved = 0;
}
*pcReturned = 1;
return TRUE;
}

C++ Windows get registry value inconsistent returns

I am writing a simple C++ program to grab a Windows registry value on a 64 bit machine.
The problem is it only works for about 50% of the registry's and for the other half "ret" does not return ERROR_SUCCESS(0).
My question is to why I am getting these inconsistent returns and also when I attempt to make the path longer than two directories it also break.
ex.
keypath = TEXT("SOFTWARE\\Perl\\ASDF");
Here is my code.
LPCTSTR keypath = TEXT("SOFTWARE\\Perl");
HKEY key = NULL;
LONG ret = ERROR_SUCCESS;
DWORD BufferSize = TOTALBYTES;
DWORD cbData;
DWORD dwRet;
DWORD type;
char registry[256] = {'\0'};
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keypath, 0, KEY_QUERY_VALUE, &key);
PPERF_DATA_BLOCK PerfData = (PPERF_DATA_BLOCK) malloc( BufferSize );
cbData = BufferSize;
if (ret == ERROR_SUCCESS)
{
dwRet = RegQueryValueEx( key,
TEXT("BinDir"),
NULL,
&type,
(LPBYTE) PerfData,
&cbData );
RegCloseKey(key);
printf("\nFinal buffer size is %d\n", BufferSize);
int i = 0;
while ((*PerfData).Signature[i] != NULL)
{
registry[i] = (char)(*PerfData).Signature[i];
i++;
}
printf("registery: %s\n", registry);
}
editing in fixes.

RegOpenKeyEx returns ERROR_NOACCESS

I am trying to read a registy key under windows 7 x64 using the following code:
static void ReadRegistryKey(HKEY hkey, TCHAR* path)
{
HKEY hkey2;
TCHAR value[MAX_PATH];
TCHAR data[4096];
const DWORD dataLength = 4096 * sizeof(TCHAR);
const DWORD valueLength = MAX_PATH+1;
DWORD returnval;
DWORD type = 0;
HLOCAL mem = LocalAlloc(LPTR, 260);
char * pc = (char*)mem;
pc++;
wchar_t* pwc = (wchar_t*)pc;
lstrcpy(pwc, path);
// Does key exist?
returnval = RegOpenKeyEx(hkey, pwc, 0 , KEY_READ | KEY_WOW64_64KEY, &hkey2);
if(returnval == ERROR_SUCCESS)
{
int i = 0;
while(returnval == ERROR_SUCCESS)
{
DWORD actualLength = dataLength;
DWORD actualValueLength = valueLength;
returnval = RegEnumValueW( hkey2,
i,
value,
&actualValueLength,
NULL,
&type,
(LPBYTE)data,
&actualLength
);
if(returnval == ERROR_NO_MORE_ITEMS)
{
_tprintf(_T("NO MORE KEYS FOUND in %s\n"), path);
break;
}
if(returnval == ERROR_SUCCESS)
{
// STUFF
}
}
}
}
When I use KEY_READ | KEY_WOW64_32KEY I get the values stored under the 32Bit registry but when I use the code above trying to read the "normal" 64bit registy I get the error code 0x3e6 (ERROR_NOACCESS)
The way i call the method:
ReadRegistryKey(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run");
What can I do to read the 64bit registry values?
Thanks
I think the allocation and pointer arithmetic of pwc is causing the problem. Pass in the path directly into the RegOpenKeyEx function.
It's also worth noting that the lstrcpy will cause a buffer overflow if path is longer than 260 bytes. Instead use StringCchCopy in Windows to give a string copy that will only copy up to the number of bytes available in the destination buffer.