I am working on getting the device id from the usb device i plug...
The above application works on win-Xp but not in Windows 7
When i try to figure it i found that CM_Get_Parent () not works...
can anybody please guide me on why its not working ...and is there any compatible function which can work on windows 7
source code:
HDEVINFO hDevInfo;
GUID guid;
BYTE buffer[ 1024 ];
DWORD dwRequiredSize ;
WCHAR buf[ 1024 ];
DEVINST devInstParent;
WCHAR volume[1024];
int aLength, anIndex;
SP_DEVICE_INTERFACE_DATA devInterfaceData;
SP_DEVINFO_DATA devInfoData;
PSP_DEVICE_INTERFACE_DETAIL_DATA pDevDetail;
guid = GUID_DEVINTERFACE_VOLUME;
hDevInfo = SetupDiGetClassDevs( &guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT );
if( hDevInfo == INVALID_HANDLE_VALUE )
{
qDebug() << "Error - SetupDiGetClassDevs failed\n";
}
for( DWORD dwIndex = 0;; dwIndex ++ )
{
ZeroMemory( &devInterfaceData, sizeof( devInterfaceData ) );
devInterfaceData.cbSize = sizeof(devInterfaceData );
if( !SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &guid,dwIndex, &devInterfaceData ) )
{
break;
}
ZeroMemory( &devInfoData, sizeof( devInfoData ) );
devInfoData.cbSize = sizeof(devInfoData);
pDevDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)buffer;
pDevDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA );
// Get device interface detail data to get Device Instance from
// SP_DEVINFO_DATA and Device Path from SP_DEVICE_INTERFACE_DETAIL_DATA
SetupDiGetDeviceInterfaceDetail(
hDevInfo,&devInterfaceData,
pDevDetail, // SP_DEVICE_INTERFACE_DETAIL_DATA
1024,&dwRequiredSize,&devInfoData ); // SP_DEVINFO_DATA
CM_Get_Parent( &devInstParent,devInfoData.DevInst, 0 );
CM_Get_Parent( &devInstParent,devInstParent, 0 );
CM_Get_Device_ID( devInstParent, buf, 1024,0 );
}
The following a litle modified code
#include <Windows.h>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <tchar.h>
#include <stdio.h>
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
#pragma comment (lib, "setupapi.lib")
int main()
{
HDEVINFO hDevInfo;
GUID guid;
BYTE buffer[ 1024 ];
DWORD dwRequiredSize ;
DEVINST devInstParent;
SP_DEVICE_INTERFACE_DATA devInterfaceData;
SP_DEVINFO_DATA devInfoData;
PSP_DEVICE_INTERFACE_DETAIL_DATA pDevDetail;
CONFIGRET status;
TCHAR szDeviceInstanceID [MAX_DEVICE_ID_LEN];
BOOL bSuccess;
guid = GUID_DEVINTERFACE_VOLUME;
hDevInfo = SetupDiGetClassDevs( &guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT );
if( hDevInfo == INVALID_HANDLE_VALUE ) {
return 1;
}
for( DWORD dwIndex = 0;; dwIndex ++ ) {
ZeroMemory( &devInterfaceData, sizeof( devInterfaceData ));
devInterfaceData.cbSize = sizeof( devInterfaceData );
if( !SetupDiEnumDeviceInterfaces( hDevInfo, NULL, &guid, dwIndex, &devInterfaceData ) ) {
DWORD dwErrorCode = GetLastError();
if (dwErrorCode == ERROR_NO_MORE_ITEMS)
break;
else
return 1; // ERROR!!!
}
ZeroMemory( &devInfoData, sizeof( devInfoData ) );
devInfoData.cbSize = sizeof( devInfoData);
pDevDetail = ( PSP_DEVICE_INTERFACE_DETAIL_DATA )buffer;
pDevDetail->cbSize = sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA );
// Get device interface detail data to get Device Instance from
// SP_DEVINFO_DATA and Device Path from SP_DEVICE_INTERFACE_DETAIL_DATA
bSuccess = SetupDiGetDeviceInterfaceDetail( hDevInfo,&devInterfaceData,
pDevDetail,// SP_DEVICE_INTERFACE_DETAIL_DATA
sizeof(buffer),&dwRequiredSize,&devInfoData ); // SP_DEVINFO_DATA
status = CM_Get_Device_ID (devInfoData.DevInst, szDeviceInstanceID, ARRAY_SIZE(szDeviceInstanceID), 0);
if (status == CR_SUCCESS) {
_tprintf (TEXT("%s\n"), szDeviceInstanceID);
}
status = CM_Get_Parent( &devInstParent,devInfoData.DevInst, 0 );
if (status == CR_SUCCESS) {
status = CM_Get_Device_ID (devInstParent, szDeviceInstanceID, ARRAY_SIZE(szDeviceInstanceID), 0);
if (status == CR_SUCCESS)
_tprintf (TEXT(" %s\n"), szDeviceInstanceID);
status = CM_Get_Parent( &devInstParent,devInstParent, 0 );
if (status == CR_SUCCESS) {
status = CM_Get_Device_ID( devInstParent, szDeviceInstanceID, ARRAY_SIZE(szDeviceInstanceID), 0);
if (status == CR_SUCCESS) {
_tprintf (TEXT(" %s\n"), szDeviceInstanceID);
}
}
else {
continue;
}
}
else {
continue;
}
}
}
work without any problem on my Windows 7 computer and produce output like following:
SCSI\CDROM&VEN_ELBY&PROD_CLONEDRIVE&REV_1.4\1&2AFD7D61&0&000000
ROOT\SCSIADAPTER\0000
HTREE\ROOT\0
STORAGE\VOLUME\{21C004DA-0EB2-11DF-8C02-806E6F6E6963}#0000000000100000
ROOT\VOLMGR\0000
HTREE\ROOT\0
STORAGE\VOLUME\{21C004DA-0EB2-11DF-8C02-806E6F6E6963}#000000004B100000
ROOT\VOLMGR\0000
HTREE\ROOT\0
STORAGE\VOLUME\{21C004DA-0EB2-11DF-8C02-806E6F6E6963}#00000037C7A00000
ROOT\VOLMGR\0000
HTREE\ROOT\0
UPDATED based on the comment: On my Windows XP in the VMware the same utility produce the following output
FDC\GENERIC_FLOPPY_DRIVE\6&1435B2E2&0&0
ACPI\PNP0700\5&324D5432&0
ACPI\PNP0A05\4&5289E18&0
IDE\CDROMNECVMWAR_VMWARE_IDE_CDR10_______________1.00____\3031303030303030303030303030303030303130
PCIIDE\IDECHANNEL\4&23686003&0&1
PCI\VEN_8086&DEV_7111&SUBSYS_197615AD&REV_01\3&61AAA01&0&39
STORAGE\VOLUME\1&30A96598&0&SIGNATURE91929192OFFSET7E00LENGTH1FFD5AA00
ROOT\FTDISK\0000
HTREE\ROOT\0
Of cause it is another computer (a virtual computer) on the other operation system and we receive another list of devices. You current question was to make the function CM_Get_Parent() working on both Windows XP and Windows 7. The posted code do this like we can see. Or it is now work correctly on your Windows XP or Windows 7 computer?
How to get pid and vid (product id and ventor id) I described in Get information about disk drives result on windows7 - 32 bit system, but for any drive letters and not for the volume devices. One can easy change the code to show more information like Volume name, Bus name and other information. But this all is already another question.
UPDATED: I placed the extended version of program from Get information about disk drives result on windows7 - 32 bit system under http://www.ok-soft-gmbh.com/ForStackOverflow/EnumMassStorage.c because of restriction with the message size in stackoverflow.com. The test output looks like http://www.ok-soft-gmbh.com/ForStackOverflow/EnumMassStorage.txt
Related
I've got a program that enumerates all processes with the Toolhelp API. With my Sysinternals Process Explorer I also can see a description of all processes. Is this description coming from the executable ? How do I get its name ?
That's my current code to enumerate the processes:
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <vector>
#include <system_error>
#include <memory>
using namespace std;
vector<PROCESSENTRY32W> getAllProcesses();
int main()
{
for( PROCESSENTRY32W &pe : getAllProcesses() )
wcout << pe.szExeFile << endl;
}
using XHANDLE = unique_ptr<void, decltype([]( HANDLE h ) { h && h != INVALID_HANDLE_VALUE && CloseHandle( h ); })>;
vector<PROCESSENTRY32W> getAllProcesses()
{
auto throwSysErr = []() { throw system_error( (int)GetLastError(), system_category(), "error enumerating processes" ); };
vector<PROCESSENTRY32W> processes;
XHANDLE xhSnapshot( CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ) );
if( xhSnapshot.get() == INVALID_HANDLE_VALUE )
throwSysErr();;
PROCESSENTRY32W pe;
pe.dwSize = sizeof pe;
if( !Process32FirstW( xhSnapshot.get(), &pe ) )
throwSysErr();
for( ; ; )
{
processes.emplace_back( pe );
pe.dwSize = sizeof pe;
if( !Process32NextW( xhSnapshot.get(), &pe ) )
if( GetLastError() == ERROR_NO_MORE_FILES )
break;
else
throwSysErr();
}
return processes;
}
#RemyLebeau 's way with code implement which is adapted from VerQueryValueA document sample. And as OpenProcess states,
If the specified process is the System Idle Process (0x00000000), the
function fails and the last error code is ERROR_INVALID_PARAMETER. If
the specified process is the System process or one of the Client
Server Run-Time Subsystem (CSRSS) processes, this function fails and
the last error code is ERROR_ACCESS_DENIED because their access
restrictions prevent user-level code from opening them.
int main()
{
TCHAR szFile[MAX_PATH] = {};
DWORD dwSize = MAX_PATH;
for (PROCESSENTRY32W& pe : getAllProcesses())
{
wcout << pe.szExeFile << endl;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,
FALSE, pe.th32ProcessID);
if (hProcess == NULL)
{
//ErrorExit(TEXT("OpenProcess"));
}
else
{
memset(szFile, 0, MAX_PATH);
dwSize = MAX_PATH;
QueryFullProcessImageName(hProcess,0, szFile,&dwSize);
DWORD s = GetFileVersionInfoSize(szFile,NULL);
if (s != 0)
{
LPVOID lpData = HeapAlloc(GetProcessHeap(), 0, s);
GetFileVersionInfo(szFile,0,s, lpData);
HRESULT hr;
UINT cbTranslate;
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
} *lpTranslate;
// Read the list of languages and code pages.
VerQueryValue(lpData,
TEXT("\\VarFileInfo\\Translation"),
(LPVOID*)&lpTranslate,
&cbTranslate);
// Read the file description for each language and code page.
LPVOID lpBuffer;
UINT dwBytes;
for (int i = 0; i < (cbTranslate / sizeof(struct LANGANDCODEPAGE)); i++)
{
TCHAR SubBlock[255] = {};
hr = StringCchPrintf(SubBlock, 50,
TEXT("\\StringFileInfo\\%04x%04x\\FileDescription"),
lpTranslate[i].wLanguage,
lpTranslate[i].wCodePage);
if (FAILED(hr))
{
// TODO: write error handler.
}
// Retrieve file description for language and code page "i".
VerQueryValue(lpData,
SubBlock,
&lpBuffer,
&dwBytes);
wcout << (TCHAR*)(lpBuffer) << endl;
}
HeapFree(GetProcessHeap(), 0, lpData);
}
//GetProcessImageFileName(hProcess, szFile, dwSize);
}
}
}
Convention wisdom to eject a USB drive on Windows is the following sequence:
CreateFile (drive letter, with read/write rights, file share read and write)
DeviceIoControl(FSCTL_LOCK_VOLUME)
DeviceIoControl(FSCTL_DISMOUNT_VOLUME)
DeviceIoControl(IOCTL_STORAGE_MEDIA_REMOVAL) PreventMediaRemoval = FALSE
DeviceIoControl(IOCTL_STORAGE_EJECT_MEDIA)
This worked fine until a recent change in Windows 10 (not sure when). Now the drive is still properly ejected, but then Windows immediately remounts the drive.
What needs to be done to eject the drive until the user removes it and puts it in again?
Using CM_Request_Device_EjectW API works for me. You can have a try.
The following is the complete code I tested and it from "How to Prepare a USB Drive for Safe Removal" at codeproject.
(Here the "F" is my USB drive letter. Replace it using your own one.)
#include <stdio.h>
#include <windows.h>
#include <Setupapi.h>
#include <winioctl.h>
#include <winioctl.h>
#include <cfgmgr32.h>
//-------------------------------------------------
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber, UINT DriveType, char* szDosDeviceName);
//-------------------------------------------------
//-------------------------------------------------
int main()
{
char DriveLetter = 'F';
DriveLetter &= ~0x20; // uppercase
if (DriveLetter < 'A' || DriveLetter > 'Z') {
return 1;
}
char szRootPath[] = "F:\\"; // "X:\" -> for GetDriveType
szRootPath[0] = DriveLetter;
char szDevicePath[] = "F:"; // "X:" -> for QueryDosDevice
szDevicePath[0] = DriveLetter;
char szVolumeAccessPath[] = "\\\\.\\F:"; // "\\.\X:" -> to open the volume
szVolumeAccessPath[4] = DriveLetter;
long DeviceNumber = -1;
// open the storage volume
HANDLE hVolume = CreateFile(szVolumeAccessPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
if (hVolume == INVALID_HANDLE_VALUE) {
return 1;
}
// get the volume's device number
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
long res = DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL);
if (res) {
DeviceNumber = sdn.DeviceNumber;
}
CloseHandle(hVolume);
if (DeviceNumber == -1) {
return 1;
}
// get the drive type which is required to match the device numbers correctely
UINT DriveType = GetDriveType(szRootPath);
// get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
char szDosDeviceName[MAX_PATH];
res = QueryDosDevice(szDevicePath, szDosDeviceName, MAX_PATH);
if (!res) {
return 1;
}
// get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
DEVINST DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber, DriveType, szDosDeviceName);
if (DevInst == 0) {
return 1;
}
PNP_VETO_TYPE VetoType = PNP_VetoTypeUnknown;
WCHAR VetoNameW[MAX_PATH];
VetoNameW[0] = 0;
bool bSuccess = false;
// get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives!
DEVINST DevInstParent = 0;
res = CM_Get_Parent(&DevInstParent, DevInst, 0);
for (long tries = 1; tries <= 3; tries++) { // sometimes we need some tries...
VetoNameW[0] = 0;
// CM_Query_And_Remove_SubTree doesn't work for restricted users
//res = CM_Query_And_Remove_SubTreeW(DevInstParent, &VetoType, VetoNameW, MAX_PATH, CM_REMOVE_NO_RESTART); // CM_Query_And_Remove_SubTreeA is not implemented under W2K!
//res = CM_Query_And_Remove_SubTreeW(DevInstParent, NULL, NULL, 0, CM_REMOVE_NO_RESTART); // with messagebox (W2K, Vista) or balloon (XP)
res = CM_Request_Device_EjectW(DevInstParent, &VetoType, VetoNameW, MAX_PATH, 0);
//res = CM_Request_Device_EjectW(DevInstParent, NULL, NULL, 0, 0); // with messagebox (W2K, Vista) or balloon (XP)
bSuccess = (res == CR_SUCCESS && VetoType == PNP_VetoTypeUnknown);
if (bSuccess) {
break;
}
Sleep(500); // required to give the next tries a chance!
}
if (bSuccess) {
printf("Success\n\n");
return 0;
}
printf("failed\n");
printf("Result=0x%2X\n", res);
if (VetoNameW[0]) {
printf("VetoName=%ws)\n\n", VetoNameW);
}
return 1;
}
//-----------------------------------------------------------
//----------------------------------------------------------------------
// returns the device instance handle of a storage volume or 0 on error
//----------------------------------------------------------------------
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber, UINT DriveType, char* szDosDeviceName)
{
bool IsFloppy = (strstr(szDosDeviceName, "\\Floppy") != NULL); // who knows a better way?
GUID* guid;
switch (DriveType) {
case DRIVE_REMOVABLE:
if (IsFloppy) {
guid = (GUID*)&GUID_DEVINTERFACE_FLOPPY;
}
else {
guid = (GUID*)&GUID_DEVINTERFACE_DISK;
}
break;
case DRIVE_FIXED:
guid = (GUID*)&GUID_DEVINTERFACE_DISK;
break;
case DRIVE_CDROM:
guid = (GUID*)&GUID_DEVINTERFACE_CDROM;
break;
default:
return 0;
}
// Get device interface info set handle for all devices attached to system
HDEVINFO hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) {
return 0;
}
// Retrieve a context structure for a device interface of a device information set
DWORD dwIndex = 0;
long res;
BYTE Buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
DWORD dwSize;
spdid.cbSize = sizeof(spdid);
while (true) {
res = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, guid, dwIndex, &spdid);
if (!res) {
break;
}
dwSize = 0;
SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &dwSize, NULL); // check the buffer size
if (dwSize != 0 && dwSize <= sizeof(Buf)) {
pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!
ZeroMemory(&spdd, sizeof(spdd));
spdd.cbSize = sizeof(spdd);
long res = SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, pspdidd, dwSize, &dwSize, &spdd);
if (res) {
// in case you are interested in the USB serial number:
// the device id string contains the serial number if the device has one,
// otherwise a generated id that contains the '&' char...
/*
DEVINST DevInstParent = 0;
CM_Get_Parent(&DevInstParent, spdd.DevInst, 0);
char szDeviceIdString[MAX_PATH];
CM_Get_Device_ID(DevInstParent, szDeviceIdString, MAX_PATH, 0);
printf("DeviceId=%s\n", szDeviceIdString);
*/
// open the disk or cdrom or floppy
HANDLE hDrive = CreateFile(pspdidd->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hDrive != INVALID_HANDLE_VALUE) {
// get its device number
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
res = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL);
if (res) {
if (DeviceNumber == (long)sdn.DeviceNumber) { // match the given device number with the one of the current device
CloseHandle(hDrive);
SetupDiDestroyDeviceInfoList(hDevInfo);
return spdd.DevInst;
}
}
CloseHandle(hDrive);
}
}
}
dwIndex++;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return 0;
}
I connect to Arduino Uno R3 via a WinAPI handle.
std::string name = "COM5";
this->handle = CreateFile(name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
How can I safely distinguish Arduino so I am not communicating with something different or at least get its name, preferably without third party libraries? After a research I found several APIs (QueryDosDevice and NtQueryObject), however I don't know how can I implement those in my code.
EDIT: I am now able to enumerate USB devices, however I have the exact opposite problem. I do not know how to CreateFile from these:
EXTERN_C const DEVPROPKEY DECLSPEC_SELECTANY DEVPKEY_Device_BusReportedDeviceDesc = { { 0x540b947e, 0x8b40, 0x45bc, { 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2 } }, 4 };
typedef BOOL(WINAPI *FN_SetupDiGetDeviceProperty)(
__in HDEVINFO DeviceInfoSet,
__in PSP_DEVINFO_DATA DeviceInfoData,
__in const DEVPROPKEY *PropertyKey,
__out DEVPROPTYPE *PropertyType,
__out_opt PBYTE PropertyBuffer,
__in DWORD PropertyBufferSize,
__out_opt PDWORD RequiredSize,
__in DWORD Flags
);
std::vector<device> usbenumerator::ListDevices()
{
DWORD dwSize;
DEVPROPTYPE ulPropertyType;
CONFIGRET status;
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
char szDeviceInstanceID[MAX_DEVICE_ID_LEN];
WCHAR szBuffer[4096];
FN_SetupDiGetDeviceProperty GetDeviceProperty = (FN_SetupDiGetDeviceProperty)(GetProcAddress(GetModuleHandle("setupapi.dll"), "SetupDiGetDevicePropertyW"));
hDevInfo = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
std::vector<device> output;
if (hDevInfo == INVALID_HANDLE_VALUE)
{
return output;
}
for (int i = 0; ; i++)
{
device dev;
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData))
{
break;
}
status = CM_Get_Device_ID(DeviceInfoData.DevInst, szDeviceInstanceID, MAX_PATH, 0);
if (status != CR_SUCCESS)
{
continue;
}
std::string deviceID = szDeviceInstanceID;
dev.id = deviceID;
if (GetDeviceProperty && GetDeviceProperty(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &ulPropertyType, (BYTE*) szBuffer, sizeof(szBuffer), &dwSize, 0))
{
if (GetDeviceProperty(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0))
{
_bstr_t b(szBuffer);
const char* cBusReportedDesc = b;
std::string busReportedDesc = cBusReportedDesc;
dev.busReportedDesc = busReportedDesc;
}
}
output.push_back(dev);
}
return output;
}
I think I got it: I am basically making my own copy of the SymbolicName key in the Windows registry (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\**YOUR_DEVICE_ID**\Device Parameters, for example HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_2341&PID_0001\75633313233351E0A1E1\Device Parameters)
// Error checking omitted
CM_Get_Device_ID(DeviceInfoData.DevInst, szDeviceInstanceID, MAX_PATH, 0);
std::string deviceID = szDeviceInstanceID;
std::string did = deviceID;
// Convert all backslashes to hash signs
std::replace(did.begin(), did.end(), '\\', '#');
// The GUID seems to stay the same: https://learn.microsoft.com/en-us/windows-hardware/drivers/install/guid-devinterface-usb-device
std::string rid = "\\??\\" + did + "#{a5dcbf10-6530-11d2-901f-00c04fb951ed}";
handle = CreateFile(rid, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
This is continuation to my previous question - phase 2 so to say.
First question was here: Fast capture stack trace on windows / 64-bit / mixed mode
Now I have resolved a huge amount of stack traces and now wondering how to resolve symbol information of managed stack frames.
For native C++ side it's relatively simple -
First you specify which process from where to take symbols:
HANDLE g_hProcess = GetCurrentProcess();
Where you can replace process in run-time using code snipet like this:
g_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_processId);
b = (g_hProcess != NULL );
if( !b )
errInfo.AppendFormat(_T("Process id '%08X' is not running anymore."), g_processId );
else
InitSymbolLoad();
And initialize symbol loading:
void InitSymbolLoad()
{
SymInitialize(g_hProcess, NULL, TRUE);
DWORD dwFlags = SymGetOptions();
SymSetOptions(SymGetOptions() | SYMOPT_DEFERRED_LOADS | SYMOPT_NO_IMAGE_SEARCH);
}
And after that resolve native symbol , somehow like this:
extern HANDLE g_hProcess;
void StackFrame::Resolve()
{
struct {
union
{
SYMBOL_INFO symbol;
char buf[sizeof(SYMBOL_INFO) + 1024];
}u;
}ImageSymbol = { 0 };
HANDLE hProcess = g_hProcess;
DWORD64 offsetFromSymbol = 0;
ImageSymbol.u.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
ImageSymbol.u.symbol.Name[0] = 0;
ImageSymbol.u.symbol.MaxNameLen = sizeof(ImageSymbol) - sizeof(SYMBOL_INFO);
SYMBOL_INFO* pSymInfo = &ImageSymbol.u.symbol;
// Get file / line of source code.
IMAGEHLP_LINE64 lineStr = { 0 };
lineStr.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
function.clear();
if( SymGetLineFromAddr64(hProcess, (DWORD64)ip, (DWORD*)&offsetFromSymbol, &lineStr) )
{
function = lineStr.FileName;
function += "(";
function += std::to_string((_ULonglong) lineStr.LineNumber).c_str();
function += "): ";
}
// Successor of SymGetSymFromAddr64.
if( SymFromAddr(hProcess, (DWORD64)ip, &offsetFromSymbol, pSymInfo) )
function += ImageSymbol.u.symbol.Name;
}
This looks like working.
But now also managed stack frames.
There are two interfaces which I've located:
IDebugClient / GetNameByOffset
Mentioned in:
http://www.codeproject.com/Articles/371137/A-Mixed-Mode-Stackwalk-with-the-IDebugClient-Inter
(*) (Includes sample code)
http://blog.steveniemitz.com/building-a-mixed-mode-stack-walker-part-1/
Used by:
https://github.com/okigan/CrashInsight (Code not touched for 4 years)
Mixed mode stackwalk article provides good example.
IXCLRDATAProcess / GetRuntimeNameByAddress
Mentioned also in two links above.
Used by process hacker (GPL license, C style)
Implementation seems to reside in here:
https://github.com/dotnet/coreclr/blob/master/src/debug/daccess/daccess.cpp
(Based on commits this code is quite alive)
ICorProfiler / ???
Mentioned at the end of (*) article.
Approach 1 seems to be quite old fashioned, also article (*) mentions some problems around it.
Approach 3 will probably require in-depth analysis of profiling API's.
There is also one mention I have found about these API's - in here:
https://naughter.wordpress.com/2015/05/24/changes-in-the-windows-10-sdk-compared-to-windows-8-1-part-two/
· cor.h, cordebug.h/idl, CorError.h, CorHdr.h, corhlpr.h,
corprof.h/idl, corpub.h/idl & corsym.h/idl: All of these header files
have been removed. They are all the native mode COM interface to .NET.
This sentence I don't fully understand. Are those interfaces dead or replaced or what happened to them ?
So I guess based on my brief analysis approach 2 is only good / alive API interface which is worth of using ? Have you came across any problems related to those api's.
After walking through huge amount of code samples and interfaces, I've understood that there aren't any simple to use API interface. Code and API's developed for native C++ works only with native C++, and code and API's developed for managed code works only with managed code.
There is additionally problem of resolving stack trace afterwards might not work. You see - developer can generate code dynamically on fly using Jit engine / IL Generator, and dispose it as well - so after you have "void*" / instruction address - you should resolve symbolic information right away, not afterwards. But I'll leave this for time being, will assume that developer is not too fancy coder and not generating and disposing new code all the times, and FreeLibrary will not be called without need. (May be I can address this later on if I'll hook FreeLibrary / Jit components.)
Resolving function name was quite trivial, through IXCLRDataProcess with little bit of magic and luck - I was able to get function names, however - I want to expand it deeper - into exact source code path and source code line where code were executing, and this turned to be quite complex functionality to reach.
Finally I've hit upon source code where such thing were performed - and it was done here:
https://github.com/dotnet/coreclr/blob/master/src/ToolBox/SOS/Strike/util.cpp
GetLineByOffset is function name in that file.
I've analyzed, retuned and made my own solution from that source code, which I'm now attaching here now:
Updated code can be found from here:
https://sourceforge.net/projects/diagnostic/
But here is just a snapshot of same code taken at some point of time:
ResolveStackM.h:
#pragma once
#include <afx.h>
#pragma warning (disable: 4091) //dbghelp.h(1544): warning C4091: 'typedef ': ignored on left of '' when no variable is declared
#include <cor.h> //xclrdata.h requires this
#include "xclrdata.h" //IXCLRDataProcess
#include <atlbase.h> //CComPtr
#include <afxstr.h> //CString
#include <crosscomp.h> //TCONTEXT
#include <Dbgeng.h> //IDebugClient
#pragma warning (default: 4091)
class ResoveStackM
{
public:
ResoveStackM();
~ResoveStackM();
void Close(void);
bool InitSymbolResolver(HANDLE hProcess, CString& lastError);
bool GetMethodName(void* ip, CStringA& methodName);
bool GetManagedFileLineInfo(void* ip, CStringA& lineInfo);
HMODULE mscordacwks_dll;
CComPtr<IXCLRDataProcess> clrDataProcess;
CComPtr<ICLRDataTarget> target;
CComPtr<IDebugClient> debugClient;
CComQIPtr<IDebugControl> debugControl;
CComQIPtr<IDebugSymbols> debugSymbols;
CComQIPtr<IDebugSymbols3> debugSymbols3;
};
//
// Typically applications don't need more than one instance of this. If you do, use your own copies.
//
extern ResoveStackM g_managedStackResolver;
ResolveStackM.cpp:
#include "ResolveStackM.h"
#include <Psapi.h> //EnumProcessModules
#include <string> //to_string
#pragma comment( lib, "dbgeng.lib" )
class CLRDataTarget : public ICLRDataTarget
{
public:
ULONG refCount;
bool bIsWow64;
HANDLE hProcess;
CLRDataTarget( HANDLE _hProcess, bool _bIsWow64 ) :
refCount(1),
bIsWow64(_bIsWow64),
hProcess(_hProcess)
{
}
HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, PVOID* ppvObject)
{
if ( IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, __uuidof(ICLRDataTarget)) )
{
AddRef();
*ppvObject = this;
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef( void)
{
return ++refCount;
}
ULONG STDMETHODCALLTYPE Release( void)
{
refCount--;
if( refCount == 0 )
delete this;
return refCount;
}
virtual HRESULT STDMETHODCALLTYPE GetMachineType( ULONG32 *machineType )
{
#ifdef _WIN64
if (!bIsWow64)
*machineType = IMAGE_FILE_MACHINE_AMD64;
else
*machineType = IMAGE_FILE_MACHINE_I386;
#else
*machineType = IMAGE_FILE_MACHINE_I386;
#endif
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetPointerSize( ULONG32* pointerSize )
{
#ifdef _WIN64
if (!bIsWow64)
#endif
*pointerSize = sizeof(PVOID);
#ifdef _WIN64
else
*pointerSize = sizeof(ULONG);
#endif
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetImageBase( LPCWSTR imagePath, CLRDATA_ADDRESS *baseAddress )
{
HMODULE dlls[1024] = { 0 };
DWORD nItems = 0;
wchar_t path[ MAX_PATH ];
DWORD whatToList = LIST_MODULES_ALL;
if( bIsWow64 )
whatToList = LIST_MODULES_32BIT;
if( !EnumProcessModulesEx( hProcess, dlls, sizeof(dlls), &nItems, whatToList ) )
{
DWORD err = GetLastError();
return HRESULT_FROM_WIN32(err);
}
nItems /= sizeof(HMODULE);
for( unsigned int i = 0; i < nItems; i++ )
{
path[0] = 0;
if( GetModuleFileNameEx(hProcess, dlls[i], path, sizeof(path) / sizeof(path[0])) )
{
wchar_t* pDll = wcsrchr( path, L'\\');
if (pDll) pDll++;
if (_wcsicmp(imagePath, path) == 0 || _wcsicmp(imagePath, pDll) == 0)
{
*baseAddress = (CLRDATA_ADDRESS) dlls[i];
return S_OK;
}
}
}
return E_FAIL;
}
virtual HRESULT STDMETHODCALLTYPE ReadVirtual( CLRDATA_ADDRESS address, BYTE *buffer, ULONG32 bytesRequested, ULONG32 *bytesRead )
{
SIZE_T readed;
if( !ReadProcessMemory(hProcess, (void*)address, buffer, bytesRequested, &readed) )
return HRESULT_FROM_WIN32( GetLastError() );
*bytesRead = (ULONG32) readed;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE WriteVirtual( CLRDATA_ADDRESS address, BYTE *buffer, ULONG32 bytesRequested, ULONG32 *bytesWritten )
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetTLSValue( ULONG32 threadID, ULONG32 index, CLRDATA_ADDRESS *value )
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE SetTLSValue( ULONG32 threadID, ULONG32 index, CLRDATA_ADDRESS value )
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetCurrentThreadID( ULONG32 *threadID )
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetThreadContext( ULONG32 threadID, ULONG32 contextFlags, ULONG32 contextSize, BYTE *context )
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE SetThreadContext( ULONG32 threadID, ULONG32 contextSize, BYTE *context)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE Request( ULONG32 reqCode, ULONG32 inBufferSize, BYTE *inBuffer, ULONG32 outBufferSize, BYTE *outBuffer)
{
return E_NOTIMPL;
}
}; //CLRDataTarget
ResoveStackM::ResoveStackM() :
mscordacwks_dll(0)
{
}
ResoveStackM::~ResoveStackM()
{
Close();
}
void ResoveStackM::Close( void )
{
clrDataProcess.Release();
target.Release();
debugClient.Release();
if( mscordacwks_dll != 0 )
{
FreeLibrary(mscordacwks_dll);
mscordacwks_dll = 0;
}
}
bool ResoveStackM::InitSymbolResolver(HANDLE hProcess, CString& lastError)
{
wchar_t path[ MAX_PATH ] = { 0 };
// According to process hacker - mscoree.dll must be loaded before loading mscordacwks.dll.
// It's enough if base application is managed.
if( GetWindowsDirectoryW(path, sizeof(path)/sizeof(wchar_t) ) == 0 )
return false; //Unlikely to fail.
#ifdef _WIN64
wcscat(path, L"\\Microsoft.NET\\Framework64\\v4.0.30319\\mscordacwks.dll");
#else
wcscat(path, L"\\Microsoft.NET\\Framework\\v4.0.30319\\mscordacwks.dll");
#endif
mscordacwks_dll = LoadLibraryW(path);
PFN_CLRDataCreateInstance pCLRCreateInstance = 0;
if( mscordacwks_dll != 0 )
pCLRCreateInstance = (PFN_CLRDataCreateInstance) GetProcAddress(mscordacwks_dll, "CLRDataCreateInstance");
if( mscordacwks_dll == 0 || pCLRCreateInstance == 0)
{
lastError.Format(L"Required dll mscordacwks.dll from .NET4 installation was not found (%s)", path);
Close();
return false;
}
BOOL isWow64 = FALSE;
IsWow64Process(hProcess, &isWow64);
target.Attach( new CLRDataTarget(hProcess, isWow64 != FALSE) );
HRESULT hr = pCLRCreateInstance(__uuidof(IXCLRDataProcess), target, (void**)&clrDataProcess );
if( FAILED(hr) )
{
lastError.Format(L"Failed to initialize mscordacwks.dll for symbol resolving (%08X)", hr);
Close();
return false;
}
hr = DebugCreate(__uuidof(IDebugClient), (void**)&debugClient);
if (FAILED(hr))
{
lastError.Format(_T("Could retrieve symbolic debug information using dbgeng.dll (Error code: 0x%08X)"), hr);
return false;
}
DWORD processId = GetProcessId(hProcess);
const ULONG64 LOCAL_SERVER = 0;
int flags = DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND;
hr = debugClient->AttachProcess(LOCAL_SERVER, processId, flags);
if (hr != S_OK)
{
lastError.Format(_T("Could attach to process 0x%X (Error code: 0x%08X)"), processId, hr);
Close();
return false;
}
debugControl = debugClient;
hr = debugControl->SetExecutionStatus(DEBUG_STATUS_GO);
if ((hr = debugControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) != S_OK)
{
return false;
}
debugSymbols3 = debugClient;
debugSymbols = debugClient;
// if debugSymbols3 == NULL - GetManagedFileLineInfo will not work
return true;
} //Init
struct ImageInfo
{
ULONG64 modBase;
};
// Based on a native offset, passed in the first argument this function
// identifies the corresponding source file name and line number.
bool ResoveStackM::GetManagedFileLineInfo( void* ip, CStringA& lineInfo )
{
ULONG lineN = 0;
char path[MAX_PATH];
ULONG64 dispacement = 0;
CComPtr<IXCLRDataMethodInstance> method;
if (!debugSymbols || !debugSymbols3)
return false;
// Get managed method by address
CLRDATA_ENUM methEnum;
HRESULT hr = clrDataProcess->StartEnumMethodInstancesByAddress((ULONG64)ip, NULL, &methEnum);
if( hr == S_OK )
{
hr = clrDataProcess->EnumMethodInstanceByAddress(&methEnum, &method);
clrDataProcess->EndEnumMethodInstancesByAddress(methEnum);
}
if (!method)
goto lDefaultFallback;
ULONG32 ilOffsets = 0;
hr = method->GetILOffsetsByAddress((CLRDATA_ADDRESS)ip, 1, NULL, &ilOffsets);
switch( (long)ilOffsets )
{
case CLRDATA_IL_OFFSET_NO_MAPPING:
goto lDefaultFallback;
case CLRDATA_IL_OFFSET_PROLOG:
// Treat all of the prologue as part of the first source line.
ilOffsets = 0;
break;
case CLRDATA_IL_OFFSET_EPILOG:
{
// Back up until we find the last real IL offset.
CLRDATA_IL_ADDRESS_MAP mapLocal[16];
CLRDATA_IL_ADDRESS_MAP* map = mapLocal;
ULONG32 count = _countof(mapLocal);
ULONG32 needed = 0;
for( ; ; )
{
hr = method->GetILAddressMap(count, &needed, map);
if ( needed <= count || map != mapLocal)
break;
map = new CLRDATA_IL_ADDRESS_MAP[ needed ];
}
ULONG32 highestOffset = 0;
for (unsigned i = 0; i < needed; i++)
{
long l = (long) map[i].ilOffset;
if (l == CLRDATA_IL_OFFSET_NO_MAPPING || l == CLRDATA_IL_OFFSET_PROLOG || l == CLRDATA_IL_OFFSET_EPILOG )
continue;
if (map[i].ilOffset > highestOffset )
highestOffset = map[i].ilOffset;
} //for
if( map != mapLocal )
delete[] map;
ilOffsets = highestOffset;
}
break;
} //switch
mdMethodDef methodToken;
void* moduleBase = 0;
{
CComPtr<IXCLRDataModule> module;
hr = method->GetTokenAndScope(&methodToken, &module);
if( !module )
goto lDefaultFallback;
//
// Retrieve ImageInfo associated with the IXCLRDataModule instance passed in. First look for NGENed module, second for IL modules.
//
for (int extentType = CLRDATA_MODULE_PREJIT_FILE; extentType >= CLRDATA_MODULE_PE_FILE; extentType--)
{
CLRDATA_ENUM enumExtents;
if (module->StartEnumExtents(&enumExtents) != S_OK )
continue;
CLRDATA_MODULE_EXTENT extent;
while (module->EnumExtent(&enumExtents, &extent) == S_OK)
{
if (extentType != extent.type )
continue;
ULONG startIndex = 0;
ULONG64 modBase = 0;
hr = debugSymbols->GetModuleByOffset((ULONG64) extent.base, 0, &startIndex, &modBase);
if( FAILED(hr) )
continue;
moduleBase = (void*)modBase;
if (moduleBase )
break;
}
module->EndEnumExtents(enumExtents);
if( moduleBase != 0 )
break;
} //for
} //module scope
DEBUG_MODULE_AND_ID id;
DEBUG_SYMBOL_ENTRY symInfo;
hr = debugSymbols3->GetSymbolEntryByToken((ULONG64)moduleBase, methodToken, &id);
if( FAILED(hr) )
goto lDefaultFallback;
hr = debugSymbols3->GetSymbolEntryInformation(&id, &symInfo);
if (FAILED(hr))
goto lDefaultFallback;
char* IlOffset = (char*)symInfo.Offset + ilOffsets;
//
// Source maps for managed code can end up with special 0xFEEFEE markers that
// indicate don't-stop points. Try and filter those out.
//
for (ULONG SkipCount = 64; SkipCount > 0; SkipCount--)
{
hr = debugSymbols3->GetLineByOffset((ULONG64)IlOffset, &lineN, path, sizeof(path), NULL, &dispacement );
if( FAILED( hr ) )
break;
if (lineN == 0xfeefee)
IlOffset++;
else
goto lCollectInfoAndReturn;
}
if( !FAILED(hr) )
// Fall into the regular translation as a last-ditch effort.
ip = IlOffset;
lDefaultFallback:
hr = debugSymbols3->GetLineByOffset((ULONG64) ip, &lineN, path, sizeof(path), NULL, &dispacement);
if( FAILED(hr) )
return false;
lCollectInfoAndReturn:
lineInfo += path;
lineInfo += "(";
lineInfo += std::to_string((_ULonglong) lineN).c_str();
lineInfo += "): ";
return true;
}
bool ResoveStackM::GetMethodName(void* ip, CStringA& symbol)
{
symbol.Empty();
GetManagedFileLineInfo(ip, symbol);
USES_CONVERSION;
CLRDATA_ADDRESS displacement = 0;
ULONG32 len = 0;
wchar_t name[1024];
if (!clrDataProcess )
return false;
HRESULT hr = clrDataProcess->GetRuntimeNameByAddress( (CLRDATA_ADDRESS)ip, 0, sizeof(name) / sizeof(name[0]), &len, name, &displacement );
if( FAILED( hr ) )
return false;
name[ len ] = 0;
symbol += W2A(name);
return true;
} //GetMethodName
ResoveStackM g_managedStackResolver;
So far tested only with some smaller piece of code, only 64-bit (doubt that 32-bit works at all - I don't have call stack determination yet for it).
It's possible that this code contains bugs, but I'll try to haunt them down and fix them.
I harvested so much code that please mark this answer as useful. :-)
Here is an answer from Jan Kotas on this:
From: Jan Kotas <jkotas#microsoft.com>
To: Tarmo Pikaro <tapika#yahoo.com>
Sent: Tuesday, January 12, 2016 5:09 AM
Subject: RE: Fast capture stack trace on windows 64 bit / mixed mode...
Your solution based on IXCLRDATAProcess sounds good to me.
PerfView (https://www.microsoft.com/en-us/download/details.aspx?id=28567) –
that does what you are trying to build as well as a lot of other stuff – is
using IXCLRDATA* as well. You may be interested in
https://github.com/Microsoft/clrmd . It is set of managed wrappers for
IXCLRDATA* that are easier to use than the COM interfaces.
What I have briefly tried out - this requires Visual Studio 2015 / C# 6.0.
Also this technique is unusable. Like .net StackTrace / StackFrame are resolving call stack and symbol information right away - and I need to resolve symbol information afterwards (after stack trace capturing).
Alternative 1 / IDebugClient / GetNameByOffset is not usable for managed stack trace, it can be used for native code only - as for native call stack I have demo code snipet above already. Not sure whether IDebugClient provides something more than SymGetLineFromAddr64 / SymFromAddr does not provide - not sure.
I've managed to get the Windows ThreadId out of the native_handle() from a boost::thread by using GetThreadId(HANDLE). Sadly that call is not available on Windows XP and after searching around I found the solution to offer als fallback support for XP by traversing all thread via Thread32First() and Thread32Next() functions of the WINAPI.
This does work somehow but my problem is I'm currently only able to identify the threads of my process... I don't now how to match the native_handle() / HANDLE from one side with the appropriate THREADENTRY32 from the loop traversal.
THREADENTRY32 te32;
//...
do {
if( te32.th32OwnerProcessID == GetCurrentProcessId() ) {
DWORD threadId = te32.th32ThreadID;
printf( "\n THREAD ID = 0x%08X", te32.th32ThreadID );
}
} while( Thread32Next(hThreadSnap, &te32 ) );
Can anyone help me with that? How do I convert a boost::thread->native_handle() to the ThreadId on WindowsXP?
Thank you very much!
Pass each thread ID in the loop to OpenThread() until you find a matching HANDLE. For example:
HANDLE hBoostThread = ...; // from boost::thread->native_handle()
DWORD dwBoostThreadID = 0;
THREADENTRY32 te32;
//...
do
{
if( te32.th32OwnerProcessID == GetCurrentProcessId() )
{
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
if (hThread != NULL)
{
if (hThread == hBoostThread)
{
CloseHandle(hThread);
dwBoostThreadID = te32.th32ThreadID;
break;
}
CloseHandle(hThread);
}
}
}
while( Thread32Next(hThreadSnap, &te32 ) );
For good measure, you can wrap this inside a function that you can call whenever GetThreadId() is not natively available so that your code does not need to know the difference, eg:
DWORD WINAPI MyGetThreadId(HANDLE Thread)
{
THREADENTRY32 te32;
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE)
return 0;
if (Thread32First(hThreadSnap, &te32))
{
do
{
HANDLE hOpenThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
if (hOpenThread != NULL)
{
if (hOpenThread == Thread)
{
CloseHandle(hOpenThread);
CloseHandle(hThreadSnap);
return te32.th32ThreadID;
}
CloseHandle(hOpenThread);
}
}
while( Thread32Next(hThreadSnap, &te32 ) );
}
CloseHandle(hThreadSnap);
return 0;
}
typedef DWORD (WINAPI *LPFN_GTID)(HANDLE);
LPFN_GTID lpGetThreadId = (LPFN_GTID) GetProcAddress(GetModuleHandle("kernel32"), "GetThreadId");
if (!lpGetThreadId)
lpGetThreadId = &MyGetThreadId;
DWORD dwThreadID = lpGetThreadId((HANDLE) boost::thread->native_handle());
With that said, a better option is to directly query the target thread itself for its own ID, instead of trying to hunt for it manually:
typedef long (WINAPI *LPFN_NTQIT)(HANDLE thread, int infoclass, void *buf, long size, long *used);
typedef struct _THREAD_BASIC_INFORMATION
{
ULONG ExitStatus;
void* TebBaseAddress;
ULONG UniqueProcessId;
ULONG UniqueThreadId;
ULONG AffinityMask;
ULONG BasePriority;
ULONG DiffProcessPriority;
} THREAD_BASIC_INFORMATION;
DWORD WINAPI MyGetThreadId(HANDLE Thread)
{
DWORD dwThreadId = 0;
HMODULE hLib = LoadLibrary("ntdll.dll");
if (hLib != NULL)
{
LPFN_NTQIT lpNtQueryInformationThread = (LPFN_NTQIT) GetProcAddress(hLib, "NtQueryInformationThread");
if (lpNtQueryInformationThread != NULL)
{
THREAD_BASIC_INFORMATION tbi = {0};
ULONG used = 0;
if (lpNtQueryInformationThread(Thread, 0, &tbi, sizeof(tbi), &used) == 0)
dwThreadId = tbi.UniqueThreadId;
}
FreeLibrary(hLib);
}
return dwThreadId;
}