LookupAccountName Always Fails With Error 122 (ERROR_INSUFFICIENT_BUFFER) - c++

Perhaps somebody can enlighten me here.
I'm attempting to automate a WiFi connection process where the SSID is determined by a serial number. Since this is always different, I figured I need to save a temporary profile each time I wish to connect.
WlanSaveTemporaryProfile() wants a LPCWSTR strAllUserProfileSecurity to define permissions for this profile. So far the rabbit hole has led me to try using LookupAccountNameW(). I have tried to AllocateAndInitializeSid() to no avail. I tried plugging in an empty buffer with the same result. In both cases, I get an error 122, which says the buffer was too small.
Any aid here is sincerely appreciated.
Here's the relevant code. Mostly constructed from examples in Microsoft's documentation.
DWORD GetStringSecurityDescriptor(
PWCHAR ps_securityDescriptor, /* This needs to be populated when this function completes. */
PULONG pul_securityDescriptorLen,
LPWSTR ps_accountName
)
{
DWORD dw_result = NULL;
DWORD dw_lastError = NULL;
DWORD dw_bufferSizeOfUserAccount = NULL;
/* Create a security descriptor for the profile. */
SECURITY_DESCRIPTOR secDesc;
bool success = InitializeSecurityDescriptor(&secDesc, SECURITY_DESCRIPTOR_REVISION);
if (!success)
{
wprintf(L"Security Descriptor Initialization Failed.\n");
}
PSID p_userSid = NULL;
/* Attempt 2: Straight up malloc the memory. Doesn't work any better.*/
//p_userSid = malloc(100);
/* Attempt 1: Allocate and Initialize an SID for LookupAccountNameW(). */
SID_IDENTIFIER_AUTHORITY auth = SECURITY_WORLD_SID_AUTHORITY;
BOOL b_sidReady = AllocateAndInitializeSid(
&auth,
6,
SECURITY_NULL_RID,
SECURITY_WORLD_RID,
SECURITY_LOCAL_RID,
SECURITY_LOCAL_LOGON_RID,
SECURITY_CREATOR_OWNER_RID,
SECURITY_CREATOR_GROUP_RID,
0, 0,
&p_userSid
);
LPDWORD buf = &dw_bufferSizeOfUserAccount;
WCHAR domainName[1000] = { 0 }; // Perhaps DNLEN + 1 was too small?
DWORD domainNameLen = 1000;
SID_NAME_USE use = SidTypeUser;
// Currently failing. dw_bufferSizeOfUserAccount still recieves a 28, so that wasn't it.
success = LookupAccountNameW(
NULL,
ps_accountName,
p_userSid,
buf,
domainName,
&domainNameLen,
&use);
if (!success)
{
dw_lastError = GetLastError();
switch (dw_lastError)
{
case ERROR_INSUFFICIENT_BUFFER: // LookupAccountNameW() always ends up here.
wprintf(L"The data area passed to a system call is too small.\n");
FreeSid(p_userSid);
return dw_lastError;
default:
wprintf(L"Looking up Account Name failed. See Error 0x%x.\n", dw_lastError);
FreeSid(p_userSid);
return dw_lastError;
}
}
// ... more code irrelevant to this problem...
}

Great thanks to Georgy Firsov!
I missed a statement in the documentation.
By calculating the size of the SID and storing it in dw_bufferSizeOfUserAccount, the function ran successfully.
dw_bufferSizeOfUserAccount = GetLengthSid(p_userSid);

Related

Stackwalk get Function Name, Line number and File Name, in debug

Quick summary
In a nut shell i wish to access the debug information regarding the stack, preferably for passing information to Logger.
I wish for the information to tell me the Function Name, Line Number and File Name.
I've got the symbols and i'm attempting to access the junk values in them and turn them in to English. However nothing seems to work.
I have commented the code for people to read and see if they can help me effectively walk the stack to pull the information out i need.
So far i can point out SymGetModuleBase() does not return a positive number only 0, according to MSDN it fails if returns 0. Which is correct as it returns a memory address.
SymGetSymFromAddr() fails to return true, which i'm assuming gets the name of the stack frame/function
SymGetLineFromAddr() goes on to fail as well and doesn't return the line number location in the file and also doesn't gather the file path.
I believe this is due to the process parameter being invalid. I will elaborate below.
Attempts to locate and fix the problem
I have read the MSDN documentation repeatedly and feel like i'm banging my head off the wall, i've done pretty much what it said and i feel like it's just not working.
However i have noticed SymInitialize() should be called prior to attempting this, which i do call. This changed the GetLastError() value from 6 ERROR_INVALID_HANDLE to 0 ERROR_SUCCESS. Yet SymGetModuleBase() still returns 0 no matter if SymInitialize() although GetLastError() reports different error codes depending on SymInitialize() use. It should return a valid virtual memory address this is where i think the main problem lies in the code.
HANDLE process = ::GetCurrentProcess(); this line in the code below returns 0xffffffffffffffff very suspect if you ask me. This should return a pseudo virtual memory address but it to me anyway looks like a false result. This happens every time i run the program which leads me to think ::GetCurrentProcess() this is either got a bug, or doesn't work somehow. According to MSDN this is the correct a up to date way of getting the current process and i don't know how to get a valid HANDLE to a the process another way. So i can't pass the first parameter in SymGetModuleBase() the correct process, although i maybe wrong.
Full code for the function
void Logger::WriteStackFrames(log::TextColor tc)
{
// Initalize some memory
DWORD machine = IMAGE_FILE_MACHINE_AMD64;
HANDLE process = ::GetCurrentProcess();
HANDLE thread = GetCurrentThread();
// Initalize more memory
CONTEXT context;
STACKFRAME stack_frame;
// Set some memory
memset(&context, 0, sizeof(CONTEXT));
memset(&stack_frame, 0, sizeof(STACKFRAME));
// Capture the context
RtlCaptureContext(&context);
// Initalize a few things here and there
stack_frame.AddrPC.Offset = context.Rip;
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrStack.Offset = context.Rsp;
stack_frame.AddrStack.Mode = AddrModeFlat;
stack_frame.AddrFrame.Offset = context.Rbp;
stack_frame.AddrFrame.Mode = AddrModeFlat;
// Randomly saw this was supposed to be called prior to StackWalk so tried it
if (!SymInitialize(process, 0, false))
{
wprintf(L"SymInitialize unable to find process!! Error: %d\r\n", GetLastError());
}
for (ULONG frame = 0; ; frame++)
{
// Set text color
SetTextColor(tc);
// Check for frames
BOOL result = StackWalk(machine, process, thread, &stack_frame, &context, 0,
SymFunctionTableAccess, SymGetModuleBase, 0);
// Get memory address of base module. Returns 0 although when SymInitialize is called before it the GetLastError returns 0 without return 6
DWORD64 module_base = SymGetModuleBase(process, stack_frame.AddrPC.Offset);
if (module_base == 0) {
wprintf(L"SymGetModuleBase is unable to get virutal address!! Error: %d\r\n", GetLastError());
}
// Initalize more memory
MODULEINFO module_info;
SecureZeroMemory(&module_info, sizeof(MODULEINFO));
// Get the file name of the file containing the function
TCHAR module_buffer[log::MaxPath];
DWORD mod_file = GetModuleFileName((HINSTANCE)module_base, module_buffer, log::MaxPath);
if ((module_base != 0) && (mod_file != 0))
{
module_info.module_name = module_buffer;
}
// Initalize more memory and clear it out
PIMAGEHLP_SYMBOL64 symbol;
IMAGEHLP_LINE64 line_num;
SecureZeroMemory(&symbol, sizeof(PIMAGEHLP_SYMBOL64));
SecureZeroMemory(&symbol, sizeof(IMAGEHLP_LINE64));
// Get the symbol
TCHAR symbol_buffer[log::MaxPath];
symbol = (PIMAGEHLP_SYMBOL)symbol_buffer;
symbol->SizeOfStruct = (sizeof(IMAGEHLP_SYMBOL) + log::MaxPath);
symbol->MaxNameLength = 254;
// Attempt to get name from symbol (fails)
LPSTR name_buffer = new CHAR[254];
if (SymGetSymFromAddr(process, stack_frame.AddrPC.Offset, 0, symbol))
{
name_buffer = symbol->Name;
}
// Set the size of something
DWORD offset = 0;
line_num.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
// Attempt to get the line and file name of where the symbol is
if (SymGetLineFromAddr(process, stack_frame.AddrPC.Offset, &offset, &line_num))
{
module_info.line = line_num.LineNumber;
module_info.file = line_num.FileName;
}
// Initalize memory
LPWSTR console_message = new TCHAR[log::MaxMsgLength];
LPWSTR file_message = new TCHAR[log::MaxMsgLength];
// Set some strings
swprintf(console_message, log::MaxMsgLength, L">> Frame %02lu: called from: %016X Stack: %016X Frame: %016X Address return: %016X\r\n",
frame, stack_frame.AddrPC.Offset, stack_frame.AddrStack.Offset, stack_frame.AddrFrame.Offset, stack_frame.AddrReturn.Offset);
swprintf(file_message, log::MaxMsgLength, L"Frame %02lu: called from: %016X Stack: %016X Frame: %016X Address return: %016X\r\n",
frame, stack_frame.AddrPC.Offset, stack_frame.AddrStack.Offset, stack_frame.AddrFrame.Offset, stack_frame.AddrReturn.Offset);
/* When the symbol can yield the name, line and file name the above strings
will also include that information */
// To go here . . .
// Write some strings
wprintf(console_message);
WriteAsync(file_message);
// Delete some memory
if (console_message) {
delete[] console_message; console_message = nullptr;
}
if (file_message) {
delete[] file_message; file_message = nullptr;
}
// If nothing else to do break loop
if (!result) {
break;
}
}
}
What i hope to achieve
Although i realize this will only work in debug mode that is fine, and i know i could write a macro using the __LINE__ __FUNCTION__ __FILE__ macros but that isn't what i'm looking for.
The results should be a wind up from the bottom stack showing the memory addresses of the calling PC, stack and frame. This works.
However it should also show me which the Name of the function, the Line number and the File path. This doesn't work.
FYI: I realize i need to add the code in to the generate the string and output it, but the code isn't capable of getting the information for the strings so that isn't coded in yet.
Please if anyone can help me, it would be fantastic all the code is focused around the "DbgHelp.h" windows file and most information is available on MSDN. So for the long question but i felt i should provide everything i know.
::GetCurrentProcess() = 0xffffffffffffffff
is not suspicious.
I tried a few variations on your code pulling bits from here and there - in the end I could not get it to work as I was using clang/mingw and it was not generating .pdb files. However, maybe the code will work for you as you are using MSVC. Anyway, here it is, in case it helps
I also noticed you hard coded machine type to AMD - I assume that is correct for you, but below I have an ifdef I found that sets it for other archs.
#include <windows.h>
#include <excpt.h>
#include <imagehlp.h>
#include <binutils/bfd.h>
#include <psapi.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdbool.h>
#include <psapi.h>
#include <dbghelp.h>
#define MAX_SYMBOL_LEN 1024
typedef struct CallstackEntry
{
DWORD64 offset; // if 0, we have no valid entry
CHAR name[MAX_SYMBOL_LEN];
CHAR undName[MAX_SYMBOL_LEN];
CHAR undFullName[MAX_SYMBOL_LEN];
DWORD64 offsetFromSmybol;
DWORD offsetFromLine;
DWORD lineNumber;
CHAR lineFileName[MAX_SYMBOL_LEN];
DWORD symType;
LPCSTR symTypeString;
CHAR moduleName[MAX_SYMBOL_LEN];
DWORD64 baseOfImage;
CHAR loadedImageName[MAX_SYMBOL_LEN];
} CallstackEntry;
typedef enum CallstackEntryType
{
firstEntry,
nextEntry,
lastEntry
} CallstackEntryType;
void _backtrace (void)
{
HANDLE process = ::GetCurrentProcess();
HANDLE thread = GetCurrentThread();
if (!SymInitialize(process, 0, true)) {
wprintf(L"SymInitialize unable to find process!! Error: %d\r\n",~
GetLastError());
}
DWORD symOptions = SymGetOptions();
symOptions |= SYMOPT_LOAD_LINES;
symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
symOptions = SymSetOptions(symOptions);
char szSearchPath[MAX_SYMBOL_LEN] = {0};
SymGetSearchPath(process, szSearchPath, MAX_SYMBOL_LEN);
char szUserName[MAX_SYMBOL_LEN] = {0};
DWORD dwSize = MAX_SYMBOL_LEN;
GetUserNameA(szUserName, &dwSize);
CHAR search_path_debug[MAX_SYMBOL_LEN];
size_t maxLen = MAX_SYMBOL_LEN;
#if _MSC_VER >= 1400
maxLen = _TRUNCATE;
#endif
_snprintf_s(search_path_debug, maxLen,~
"SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n",
szSearchPath, symOptions, szUserName);
search_path_debug[MAX_SYMBOL_LEN - 1] = 0;
printf(search_path_debug);
// Initalize more memory
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
// Initalize a few things here and there
STACKFRAME stack;
memset(&stack, 0, sizeof(STACKFRAME));
stack.AddrPC.Offset = context.Rip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = context.Rsp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = context.Rbp;
stack.AddrFrame.Mode = AddrModeFlat;
#ifdef _M_IX86
auto machine = IMAGE_FILE_MACHINE_I386;
#elif _M_X64
auto machine = IMAGE_FILE_MACHINE_AMD64;
#elif _M_IA64
auto machine = IMAGE_FILE_MACHINE_IA64;
#else
#error "platform not supported!"
#endif
for (ULONG frame = 0; ; frame++) {
BOOL result = StackWalk(machine,~
process,~
thread,~
&stack,
&context,
0,
SymFunctionTableAccess,~
SymGetModuleBase,~
0);
CallstackEntry csEntry;
csEntry.offset = stack.AddrPC.Offset;
csEntry.name[0] = 0;
csEntry.undName[0] = 0;
csEntry.undFullName[0] = 0;
csEntry.offsetFromSmybol = 0;
csEntry.offsetFromLine = 0;
csEntry.lineFileName[0] = 0;
csEntry.lineNumber = 0;
csEntry.loadedImageName[0] = 0;
csEntry.moduleName[0] = 0;
IMAGEHLP_SYMBOL64 symbol {};
symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol.MaxNameLength = MAX_SYMBOL_LEN;
// Initalize more memory and clear it out
if (SymGetSymFromAddr64(process,~
stack.AddrPC.Offset,
&csEntry.offsetFromSmybol,~
&symbol)) {
}
IMAGEHLP_LINE64 line {};
line.SizeOfStruct = sizeof(line);
if (SymGetLineFromAddr64(process,~
stack.AddrPC.Offset,
&csEntry.offsetFromLine,~
&line)) {
}
printf("Frame %lu:\n"
" Symbol name: %s\n"
" PC address: 0x%08LX\n"
" Stack address: 0x%08LX\n"
" Frame address: 0x%08LX\n"
"\n",
frame,
symbol.Name,
(ULONG64)stack.AddrPC.Offset,
(ULONG64)stack.AddrStack.Offset,
(ULONG64)stack.AddrFrame.Offset
);
// If nothing else to do break loop
if (!result) {
break;
}
}
}

How to get DHCPV6 server IP using C++ program. I tried using DHCPv6RequestParams but always gets error code 121

In Dhcpv6RequestParams i used it's first parameter as TRUE & FALSE. but I never get server response with it's IP address.
y code is as follows:
BOOL RetrieveDHCPInfo(LPWSTR pszAdapterName)
{
DWORD dwError;
int num = 0;
CHAR TmpBufferV6[1000];
DWORD initPtr = 0;
Dhcpv6CApiInitialize(&initPtr);
DHCPV6CAPI_PARAMS DhcpApiServerNameParamsV6 = { 0,
DHCPV6_OPTION_SERVERID,
FALSE,
NULL,
0
};
DHCPV6CAPI_PARAMS paramArrayV6[1] = {
DhcpApiServerNameParamsV6
};
DHCPV6CAPI_PARAMS_ARRAY DhcpV6ApiParamsArray = {
1, // only one option to request
paramArrayV6
};
DWORD size = sizeof(TmpBufferV6);
dwError = Dhcpv6RequestParams(TRUE,
NULL,
pszAdapterName,
NULL,
DhcpV6ApiParamsArray,
(PBYTE)TmpBufferV6,
&size);
}
// I am receiving errcode as 121(Semaphore timed out).
Contacted microsoft regarding this. MSFT told that yet they have not provided any API which will return the DHCPv6 server address.
It is possible that in next releases they may give some API for the same.

What is the preferred way to get a device path for CreateFile() in a UWP C++ App?

I am converting a project to a UWP App, and thus have been following guidelines outlined in the MSDN post here. The existing project heavily relies on CreateFile() to communicate with connected devices.
There are many posts in SO that show us how to get a CreateFile()-accepted device path using SetupAPI's SetupDiGetDeviceInterfaceDetail() Is there an alternative way to do this using the PnP Configuration Manager API? Or an alternative, user-mode way at all?
I had some hope when I saw this example in Windows Driver Samples github, but quickly became dismayed when I saw that the function they used in the sample is ironically not intended for developer use, as noted in this MSDN page.
function GetDevicePath in general correct and can be used as is. about difference between CM_*(..) and CM_*_Ex(.., HMACHINE hMachine) - the CM_*(..) simply call CM_*_Ex(.., NULL) - so for local computer versions with and without _Ex suffix the same.
about concrete GetDevicePath code - call CM_Get_Device_Interface_List_Size and than CM_Get_Device_Interface_List only once not 100% correct - because between this two calls new device with this interface can be arrived to system and buffer size returned by CM_Get_Device_Interface_List_Size can be already not enough for CM_Get_Device_Interface_List. of course possibility of this very low, and you can ignore this. but i prefer make code maximum theoretical correct and call this in loop, until we not receive error other than CR_BUFFER_SMALL. also need understand that CM_Get_Device_Interface_List return multiple, NULL-terminated Unicode strings - so we need iterate here. in [example] always used only first returned symbolic link name of an interface instance. but it can be more than 1 or at all - 0 (empty). so better name function - GetDevicePaths - note s at the end. i be use code like this:
ULONG GetDevicePaths(LPGUID InterfaceClassGuid, PWSTR* pbuf)
{
CONFIGRET err;
ULONG len = 1024;//first try with some reasonable buffer size, without call *_List_SizeW
for(PWSTR buf;;)
{
if (!(buf = (PWSTR)LocalAlloc(0, len * sizeof(WCHAR))))
{
return ERROR_NO_SYSTEM_RESOURCES;
}
switch (err = CM_Get_Device_Interface_ListW(InterfaceClassGuid, 0, buf, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
case CR_BUFFER_SMALL:
err = CM_Get_Device_Interface_List_SizeW(&len, InterfaceClassGuid, 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
default:
LocalFree(buf);
if (err)
{
return CM_MapCrToWin32Err(err, ERROR_UNIDENTIFIED_ERROR);
}
continue;
case CR_SUCCESS:
*pbuf = buf;
return NOERROR;
}
}
}
and usage example:
void example()
{
PWSTR buf, sz;
if (NOERROR == GetDevicePaths((GUID*)&GUID_DEVINTERFACE_VOLUME, &buf))
{
sz = buf;
while (*sz)
{
DbgPrint("%S\n", sz);
HANDLE hFile = CreateFile(sz, FILE_GENERIC_READ, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
// do something
CloseHandle(hFile);
}
sz += 1 + wcslen(sz);
}
LocalFree(buf);
}
}
so we must not simply use in returned DevicePathS (sz) only first string, but iterate it
while (*sz)
{
// use sz
sz += 1 + wcslen(sz);
}
I got a valid Device Path to a USB Hub Device, and used it successfully to get various device descriptors by sending some IOCTLs, by using the function I posted in my own answer to another question
I'm reporting the same function below:
This function returns a list of NULL-terminated Device Paths (that's what we get from CM_Get_Device_Interface_List())
You need to pass it the DEVINST, and the wanted interface GUID.
Since both the DEVINST and interface GUID are specified, it is highly likely that CM_Get_Device_Interface_List() will return a single Device Path for that interface, but technically you should be prepared to get more than one result.
It is responsibility of the caller to delete[] the returned list if the function returns successfully (return code 0)
int GetDevInstInterfaces(DEVINST dev, LPGUID interfaceGUID, wchar_t**outIfaces, ULONG* outIfacesLen)
{
CONFIGRET cres;
if (!outIfaces)
return -1;
if (!outIfacesLen)
return -2;
// Get System Device ID
WCHAR sysDeviceID[256];
cres = CM_Get_Device_ID(dev, sysDeviceID, sizeof(sysDeviceID) / sizeof(sysDeviceID[0]), 0);
if (cres != CR_SUCCESS)
return -11;
// Get list size
ULONG ifaceListSize = 0;
cres = CM_Get_Device_Interface_List_Size(&ifaceListSize, interfaceGUID, sysDeviceID, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (cres != CR_SUCCESS)
return -12;
// Allocate memory for the list
wchar_t* ifaceList = new wchar_t[ifaceListSize];
// Populate the list
cres = CM_Get_Device_Interface_List(interfaceGUID, sysDeviceID, ifaceList, ifaceListSize, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (cres != CR_SUCCESS) {
delete[] ifaceList;
return -13;
}
// Return list
*outIfaces = ifaceList;
*outIfacesLen = ifaceListSize;
return 0;
}
Please note that, as RbMm already said in his answer, you may get a CR_BUFFER_SMALL error from the last CM_Get_Device_Interface_List() call, since the device list may have been changed in the time between the CM_Get_Device_Interface_List_Size() and CM_Get_Device_Interface_List() calls.

Where is comsupp(w).lib?

I'm trying to compile some COM code, the example here. I get the compilation fine, but linking complaints about ConvertStringtoBSTR missing. After doing some research, I found out that said symbol should be in comsupp.lib. The problem is that I can not find this library in the Windows SDK... where is the library or the function?
Just copy the comment from #HansPassant so as to make the comment to become an answer. Not trying to steal his answer, but just want to let people know there is an answer to this question.
It is not an SDK file, it is a Visual Studio file. Stored in the vc/lib directory. VS license required. – Hans Passant Feb 25 '12 at 19:09
Seven years later in case of googling ... If you don't have VS, you may just copy and use the code of the WineHQ source for this function, something like that :
char* WINAPI ConvertBSTRToString(BSTR pSrc)
{
DWORD cb, cwch;
char *szOut = NULL;
if (!pSrc) return NULL;
/* Retrieve the size of the BSTR with the NULL terminator */
cwch = ::SysStringLen(pSrc) + 1;
/* Compute the needed size with the NULL terminator */
cb = ::WideCharToMultiByte(CP_ACP, 0, pSrc, cwch, NULL, 0, NULL, NULL);
if (cb == 0)
{
cwch = ::GetLastError();
::_com_issue_error(!IS_ERROR(cwch) ? HRESULT_FROM_WIN32(cwch) : cwch);
return NULL;
}
/* Allocate the string */
szOut = (char*)::operator new(cb * sizeof(char));
if (!szOut)
{
::_com_issue_error(HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY));
return NULL;
}
/* Convert the string and NULL-terminate */
szOut[cb - 1] = '\0';
if (::WideCharToMultiByte(CP_ACP, 0, pSrc, cwch, szOut, cb, NULL, NULL) == 0)
{
/* We failed, clean everything up */
cwch = ::GetLastError();
::operator delete(szOut);
szOut = NULL;
::_com_issue_error(!IS_ERROR(cwch) ? HRESULT_FROM_WIN32(cwch) : cwch);
}
return szOut;
}

How do I read from a version resource in Visual C++

I have a version resource in my resources in a C++ project which contains version number, copyright and build details. Is there an easy way to access this at run-time to populate my help/about dialog as I am currently maintaining seperate const values of this information. Ideally, the solution should work for Windows/CE mobile and earlier versions of Visual C++ (6.0 upwards).
This is an edited version of my original answer.
bool GetProductAndVersion(CStringA & strProductName, CStringA & strProductVersion)
{
// get the filename of the executable containing the version resource
TCHAR szFilename[MAX_PATH + 1] = {0};
if (GetModuleFileName(NULL, szFilename, MAX_PATH) == 0)
{
TRACE("GetModuleFileName failed with error %d\n", GetLastError());
return false;
}
// allocate a block of memory for the version info
DWORD dummy;
DWORD dwSize = GetFileVersionInfoSize(szFilename, &dummy);
if (dwSize == 0)
{
TRACE("GetFileVersionInfoSize failed with error %d\n", GetLastError());
return false;
}
std::vector<BYTE> data(dwSize);
// load the version info
if (!GetFileVersionInfo(szFilename, NULL, dwSize, &data[0]))
{
TRACE("GetFileVersionInfo failed with error %d\n", GetLastError());
return false;
}
// get the name and version strings
LPVOID pvProductName = NULL;
unsigned int iProductNameLen = 0;
LPVOID pvProductVersion = NULL;
unsigned int iProductVersionLen = 0;
// replace "040904e4" with the language ID of your resources
if (!VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductName"), &pvProductName, &iProductNameLen) ||
!VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductVersion"), &pvProductVersion, &iProductVersionLen))
{
TRACE("Can't obtain ProductName and ProductVersion from resources\n");
return false;
}
strProductName.SetString((LPCSTR)pvProductName, iProductNameLen);
strProductVersion.SetString((LPCSTR)pvProductVersion, iProductVersionLen);
return true;
}
To get a language independent result to Mark's answer change :
// replace "040904e4" with the language ID of your resources
!VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductVersion"), &pvProductVersion, &iProductVersionLen))
{
TRACE("Can't obtain ProductName and ProductVersion from resources\n");
return false;
}
To
UINT uiVerLen = 0;
VS_FIXEDFILEINFO* pFixedInfo = 0; // pointer to fixed file info structure
// get the fixed file info (language-independent)
if(VerQueryValue(&data[0], TEXT("\\"), (void**)&pFixedInfo, (UINT *)&uiVerLen) == 0)
{
return false;
}
strProductVersion.Format("%u.%u.%u.%u",
HIWORD (pFixedInfo->dwProductVersionMS),
LOWORD (pFixedInfo->dwProductVersionMS),
HIWORD (pFixedInfo->dwProductVersionLS),
LOWORD (pFixedInfo->dwProductVersionLS));
Something like might get you started, perhaps:
TCHAR moduleName[MAX_PATH+1];
(void)GetModuleFileName(AfxGetInstanceHandle(), moduleName, MAX_PATH);
DWORD dummyZero;
DWORD versionSize = GetFileVersionInfoSize(moduleName, &dummyZero);
if(versionSize == 0)
{
return NULL;
}
void* pVersion = malloc(versionSize);
if(pVersion == NULL)
{
return NULL;
}
if(!GetFileVersionInfo(moduleName, NULL, versionSize, pVersion))
{
free(pVersion);
return NULL;
}
UINT length;
VS_FIXEDFILEINFO* pFixInfo;
VERIFY(VerQueryValue(pVersionInfo, const_cast<LPTSTR>("\\"), (LPVOID*)&pFixInfo, &length));
Something like this will give you raw access to the resource data and get you started:
HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(MY_VERSION_ID), RT_VERSION);
DWORD size = ::SizeofResource(NULL, res);
HGLOBAL mem = ::LoadResource(NULL, res);
LPVOID raw_data = ::LockResource(mem);
...
::FreeResource(mem);
Beware!
Using FindResource..LockResource is not correct. It will sometimes work (as it did in my small demo program) and sometimes cause access violations (example: the production code I was making the demo for).
The VerQueryValue() documentation states that you should call GetFileVersionInfoSize and GetFileVersionInfo instead.
Raymond Chen explains, see http://blogs.msdn.com/oldnewthing/archive/2006/12/26/1365215.aspx
Ok, a bit more googleing found the following on CodeGuru. Basically this approach uses the CFileVersionInfo object to get on any given file. It should be interesting to see if it works on the currently running .EXE file and on Windows CE.
Sometimes I receive Access Violation when use VerQueryValueA. But I never got this error when use VerQueryValueW. I think something wrong with VerQueryValueA in version.dll. Therefore I use VerQueryValueW instead of VerQueryValueA even in projects Multi-byte Character Encoding. Here is my code of ReadVersion function