I am making a simple Application to collect and represent data over bluetooth serial COM port and I don't want to hard-code the COM ports. So I want to enumerate the COM ports by looking up HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM. However I'm fairly new to both wxWidgets and win32 registry terminology(I still don't understand what's supposed to be the "name" and "value" in wxWidgets documentation, they don't work as expected). What I want is to iterate through all the COMM ports and add them to a drop down list. This doesn't work:
wxRegKey regKey(wxRegKey::HKLM,"HARDWARE\\DEVICEMAP\\SERIALCOMM");
size_t subkeys;
long k = 1;
regKey.Open(wxRegKey::Read);
regKey.GetKeyInfo(&subkeys, NULL, NULL, NULL);
wxString key_name;
regKey.GetFirstValue(key_name, k);
m_drop_down->Append(key_name);
for (int i = 0; i < subkeys; i++) {
regKey.GetNextValue(key_name, k);
m_drop_down->Append(key_name);
}
regKey.Close();
It only appends one \Device\BthModem2 to the dropdown list. I would be grateful if someone cleared the terminologies up and tell me how I should go about making it work. For reference, here's what that registry entry looks to me, I want COM3, COM4, COM5, COM6 appended to the drop down list:
If you want to get the "value" (the thing in the right column), you can declare a function like this:
wxString GetStringData(const wxRegKey& regKey, const wxString& valueName)
{
wxRegKey::ValueType vtype = regKey.GetValueType(valueName);
wxString data;
if ( vtype == wxRegKey::Type_String )
{
regKey.QueryValue(valueName, data, true);
}
return data;
}
To get the complete list of them from the registry entry, you can iterate over the values in the registry entry like this:
while(regKey.GetNextValue(key_name, k)) {
wxString data = GetStringData(regKey,valueName);
if ( !data.IsEmpty() )
{
m_drop_down->Append(key_name);
}
}
GetNextValue will return false when there are no more values to be fetched breaking out of the loop.
This isn't really complete since this won't distinguish between a registry value whose type isn't string and a registy value whose type is a string but with empty data, but it should be clear how to modify the function and/or the iteration if it's necessary to make that distinction.
According to MSDN:Enumerating Registry Subkeys and RegGetValue, RegGetValue retrieves the type and data for the specified registry value.
Code:
int main()
{
long ret;
unsigned long reg_index = 0;
DWORD dwValSize = MAX_VALUE_NAME;
DWORD dwDataSize = MAX_VALUE_NAME;
LPTSTR lpValname = new TCHAR[MAX_VALUE_NAME]{};
LPTSTR lpDataname = new TCHAR[MAX_VALUE_NAME]{};
HKEY hkey;
int result = RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft"), 0, KEY_READ, &hkey);
if (result == ERROR_SUCCESS) {
while ((ret = RegEnumValue(hkey, reg_index++, lpValname, &dwValSize, NULL, NULL, NULL, NULL)) != ERROR_NO_MORE_ITEMS)
{
if ((ret == ERROR_SUCCESS) || (ret == ERROR_MORE_DATA)) {
_tprintf(TEXT("(%d) %s"), reg_index, lpValname);
DWORD type = 0;
if (RegGetValue(hkey, 0,lpValname, RRF_RT_ANY, &type, lpDataname, &dwDataSize) == ERROR_SUCCESS)
{
_tprintf(TEXT("(Value data %s)\n"), lpDataname);
}
}
dwValSize = MAX_VALUE_NAME;
dwDataSize = MAX_VALUE_NAME;
ZeroMemory(lpDataname,sizeof(TCHAR)* dwDataSize);
ZeroMemory(lpValname,sizeof(TCHAR)* dwValSize);
}
RegCloseKey(hkey);
}
return TRUE;
}
Related
I'm trying to get the last opened directory by an open file dialog, and it seems that we can get it by first retrieving the key's name that contains the path using the first BYTE of the MRUListEx key, then the path can be obtained by reading the value of this key's name.
MRUListEx key can be find at: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU.
The problem is that I don't know how to properly fill the "SHITEMID" structure. (it doesn't let me access the index, and it throws a Memory access violation). I don't even know if the code below is valid in any points.
Sorry, the code is very dirty for the moment but I'll revamps it when I finally find what causes these errors.
void MyClass::OnDocumentSave(bool showSaveDlg)
{
// Do something that is not relevant here...
try {
HKEY hKey = NULL;
std::wstring regPath = LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU\)";
LSTATUS statusCode = RegOpenKeyExW(HKEY_CURRENT_USER, regPath.data(), 0, KEY_QUERY_VALUE, &hKey);
if (statusCode != ERROR_SUCCESS)
throw std::exception(std::string("Unable to open the specified registry key, sys err code: ") + std::to_string(statusCode));
BYTE data[MAX_PATH];
DWORD bufferSize = MAX_PATH;
statusCode = RegGetValueW(hKey, L"", L"MRUListEx", RRF_RT_REG_BINARY, NULL, &data, &bufferSize);
if (statusCode != ERROR_SUCCESS)
throw std::runtime_error(std::string("Failed at RegGetValue() Sys error code: ") + std::to_string(statusCode));
// Please note that the buffer has intentionally a fixed size here and everything is
// simplified for readability, but it uses dynamic memory allocation in the real code to
// handle errors such as ERROR_MORE_DATA
BYTE* pathData[512];
bufferSize = 512;
DWORD type = 0; // In case it matters, the returned value is 3
statusCode = RegGetValueW(hKey, L"", std::to_wstring(data[0]).c_str(), RRF_RT_REG_BINARY, &type, &pathData, &bufferSize);
if (statusCode != ERROR_SUCCESS)
throw std::runtime_error(std::string("Failed at RegGetValue() Sys error code: ") + std::to_string(statusCode));
// I don't know how to fill this structure, the documentation is very minimal,
// and I don't understand it.
int offset = sizeof(APPNAME);
SHITEMID shellIDList[2]{
// Throw a memory access violation at 0x*****, the debugger can't seems to
// get anything in pathData.
{ sizeof(USHORT) + sizeof(pathData), *pathData[0 + offset] },
{ 0, 0 } };
ITEMIDLIST idl{ shellIDList[0] };
// This is supposed give me the last path that was opened by a File Picker.
SHGetPathFromIDListW(&idl, initialDir.data());
}
catch (std::exception& e) {
// Silently set the initial directory to a hard-coded path instead of getting the registry value.
}
}
You don't seem to understand how types and simple arrays work! BYTE* pathData[512]; is not a 512 byte buffer. Use BYTE pathData[512];.
After reading into pathData, call SHGetPathFromIDList((PCIDLIST_ABSOLUTE) pathData, ...);.
That being said, that ComDlg32 key is undocumented and I don't think it stores a pidl so even if your code is corrected it is not going to work.
EnumMRUListW is a documented function you can call but it is not going to help you decode the data.
It looks to me like the location might be prefixed with the name of the .exe so as a minimum you would have to skip (lstrlenW(pathData)+1)*2 bytes before you find the real data...
If you're going for undocumented stuff, here is a code that can dump an MRU list like the LastVisitedPidlMRU one:
// the Shell object that can read MRU lists
static GUID CLSID_MruLongList = { 0x53bd6b4e,0x3780,0x4693,{0xaf,0xc3,0x71,0x61,0xc2,0xf3,0xee,0x9c} };
typedef enum MRULISTF
{
MRULISTF_USE_MEMCMP = 0x0,
MRULISTF_USE_STRCMPIW = 0x1,
MRULISTF_USE_STRCMPW = 0x2,
MRULISTF_USE_ILISEQUAL = 0x3,
};
typedef int (__stdcall *MRUDATALISTCOMPARE)(const BYTE*, const BYTE*, int);
MIDL_INTERFACE("00000000-0000-0000-0000-000000000000") // unknown guid but we don't care
IMruDataCompare : public IUnknown
{
public:
virtual HRESULT CompareItems(const BYTE*, int, const BYTE*, int) = 0;
};
MIDL_INTERFACE("d2c22919-91f5-4284-8807-58a2d64e561c")
IMruDataList2 : public IUnknown
{
public:
virtual HRESULT InitData(UINT uMax, MRULISTF flags, HKEY hKey, LPCWSTR pszSubKey, MRUDATALISTCOMPARE pfnCompare) = 0;
virtual HRESULT AddData(const BYTE* pData, DWORD cbData, DWORD* pdwSlot) = 0;
virtual HRESULT InsertData(const BYTE*, DWORD cbData, int* piIndex, DWORD* pdwSlot) = 0;
virtual HRESULT FindData(const BYTE* pData, DWORD cbData, int* piIndex) = 0;
virtual HRESULT GetData(int iIndex, BYTE* pData, DWORD cbData) = 0;
virtual HRESULT QueryInfo(int iIndex, DWORD* pdwSlot, DWORD* pcbData) = 0;
virtual HRESULT Delete(int iIndex) = 0;
virtual HRESULT InitData2(UINT uMax, MRULISTF flags, HKEY hKey, LPCWSTR pszSubKey, IMruDataCompare* pfnCompare) = 0;
};
int main()
{
CoInitialize(NULL);
{
CComPtr<IMruDataList2> mru;
if (SUCCEEDED(mru.CoCreateInstance(CLSID_MruLongList)))
{
const int max = 100; // get max 100 entries
mru->InitData(max, MRULISTF_USE_MEMCMP, HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\LastVisitedPidlMRU\\", nullptr);
for (auto i = 0; i < max; i++)
{
DWORD slot;
DWORD size;
// get size
if (FAILED(mru->QueryInfo(i, &slot, &size)))
continue;
// get data
// note beginning is a LPWSTR containing exe data
auto data = (LPBYTE)_alloca(size);
if (FAILED(mru->GetData(i, data, size)))
continue;
// the rest is a PIDL
auto pidl = (LPCITEMIDLIST)(data + (lstrlen((LPWSTR)data) + 1) * 2);
// get the shell item
CComPtr<IShellItem> item;
if (SUCCEEDED(SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&item))))
{
// get its path
CComHeapPtr<WCHAR> path;
item->GetDisplayName(SIGDN::SIGDN_DESKTOPABSOLUTEPARSING, &path);
wprintf(L"Executable: %s LastVisited: %s\n", data, path);
}
}
}
}
CoUninitialize();
}
PS: I'm using ATL's smart classes
I finally figured out how to create an ITEMIDLIST structure, and it all works well.
Explanations:
After some test, I've concluded that this value is not necessarily updated. The application that uses a file picker needs to write to the registry to reflect changes.
For example, VS Community 2022 / VS Code updates this value, but the new Microsoft Notepad does not (not sure for the old one). Apparently, .NET applications seem to be updated automatically, though.
First, we obviously must open the key at Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU.
The stored data value we need is on one of the values, which has a number as a name.
The first byte of the MRUListEx value lies the last path.
HKEY hKey = NULL;
std::wstring regMRUPath = LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU\)";
LSTATUS statusCode = RegOpenKeyExW(HKEY_CURRENT_USER, regMRUPath.data(), 0, KEY_QUERY_VALUE, &hKey);
if (statusCode != ERROR_SUCCESS)
// Handle error
BYTE data[MAX_PATH];
DWORD bufferSize = MAX_PATH;
statusCode = RegGetValueW(hKey, L"", L"MRUListEx", RRF_RT_REG_BINARY, NULL, &data, &bufferSize);
if (statusCode != ERROR_SUCCESS)
// Handle error
std::wstring keyName = std::to_wstring(data[0]);
Now that we have the value, we can get its data.
DWORD reqBuffSize = 0;
statusCode = RegQueryValueExW(hKey, keyName.data(), NULL, NULL, NULL, &reqBuffSize);
if (statusCode != ERROR_SUCCESS)
// maybe the specified key is invalid or missing.
BYTE pathData[reqBuffSize];
statusCode = RegGetValueW(hKey, L"", keyName.data(), RRF_RT_REG_BINARY, NULL, &pathData, &reqBuffSize);
if (statusCode != ERROR_SUCCESS)
// maybe the specified key is invalid or missing.
We have another problem: The path is not directly accessible. The executable name is prefixed from the real path, so we must remove it.
Actually, it's very easy. The delimiter between the executable name and the real path is \0\0\0.
const size_t offset = (lstrlenW(reinterpret_cast<LPCWSTR>(pathData)) * sizeof(wchar_t))
+ (2 * sizeof(wchar_t));
The obtained value is not human-readable, but it actually stores a PIDL, so we can get it with SHGetPathFromIDList. The ITEMIDList is not very well documented on the Microsoft's website, but here's a way to create one of these things:
IMalloc* pMalloc;
if (FAILED(SHGetMalloc(&pMalloc)))
// handle error, failed to get a pointer to the IMalloc interface.
PIDLIST_ABSOLUTE pIdl = (PIDLIST_ABSOLUTE)pMalloc->Alloc((reqBuffSize - offset) + 1);
pIdl->mkid.cb = (sizeof(USHORT) + (reqBuffSize - offset));
memcpy_s(pIdl->mkid.abID, (reqBuffSize - offset) + 1, pathData + offset, (reqBuffSize - offset) + 1);
PIDLIST_ABSOLUTE pNext = ILGetNext(pIdl);
if (!pNext)
// handle error, failed to get a pointer to the next item
pNext->mkid.cb = 0;
initialDir.reserve(MAX_PATH * 2);
The ITEMIDList structure has two members: cb and abID. cb is the size of a USHORT + the size of the data, and abID is a pointer to the data itself.
The last ITEMIDList struct must have cb set to zero to mark the end. ILGetNext gives us a pointer to the next element.
// Finally, return the path!
std::wstring retValue;
retValue.reserve(MAX_PATH);
if (FALSE == SHGetPathFromIDList(pIdl, initialDir.data())) {
// oh no! free the allocated memory and throw an exception
}
pMalloc->Free(pIdl);
It's not as hard as it seems to be at the beginning, but if you want to get this value for use with a file picker, it's recommended to use COM instead since it's easier and less error-prone. Thank tou all for tour response and have a good day!
If you want to get the path from a specified extension, see the OpenSavePidlMRU key. It works the same, but it requires some little adjustments.
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?
This question already has an answer here:
c++ get registry key returns only one char
(1 answer)
Closed 9 years ago.
I am trying to invoke a binary from a service program. The path of the binary is fetched from a registry key. The path only contains "C" when I assign the buffer to the string. Where am I commiting the mistake. I am using visual studio 2010Here is the code:
std::string path;
getPathFromKey(path);
path = path+"\\myapp.exe";
argument = "start \"\" \""+path+"\"";
system(argument.c_str());
/* retrieving key */
void getPathFromKey(std::string &path)
{
HKEY hKey = 0;
char buf[512];
DWORD dwType = 0;
DWORD dwBufSize = sizeof(buf);
if( RegOpenKey(HKEY_LOCAL_MACHINE,L"SOFTWARE\\MYAPP\\MyApp",&hKey) == ERROR_SUCCESS)
{
dwType = REG_SZ;
if( RegQueryValueEx(hKey,L"InstallPath",0, &dwType, (LPBYTE)buf, &dwBufSize) == ERROR_SUCCESS)
{
for(int i=0;buf[i];i++)
path[i]=buf[i];
return;
}
else
{
RegCloseKey(hKey);
return;
}
}
else if( RegOpenKey(HKEY_LOCAL_MACHINE,L"Software\\Wow6432Node\\MYAPP\\MyApp",&hKey) == ERROR_SUCCESS)
{
dwType = REG_SZ;
if( RegQueryValueEx(hKey,L"InstallPath",0, &dwType, (BYTE*)buf, &dwBufSize) == ERROR_SUCCESS)
{
for(int i=0;buf[i];i++)
path[i]=buf[i];
return;
}
else
{
RegCloseKey(hKey);
return;
}
}
}
I get the following error:
Windows cannot find "C"
If you get letter C, that means your code is partially working. However, this:
for(int i=0;buf[i];i++)
path[i]=buf[i];
makes no sense, at least not for me, because I can't even image how this cycle would work (given that you get letter C probably means that it runs once). A simple solution would be to use this:
path = std::string(buf);
If I am wrong then check buf value before return, while debugging.
I'm trying to add my software to registry, I have found some pieces of the codes I can use but not full working code C/C++ is new to me and can't create it on my own. But here is the basic idea: Check if reg key set if not create it.
I was able to get my program location using this code:
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL,szPath,MAX_PATH);
And was able to create the key with: (Not sure if it's the right way)
HKEY newValue;
RegOpenKey(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&newValue);
RegSetValueEx(newValue,"myprogram",0,REG_SZ,(LPBYTE)szPath,sizeof(szPath));
RegCloseKey(newValue);
return 0;
What is missing, A small check if the key isn't already there...
Thank you!
Here's some code that likely does what you want. Call RegisterProgram for your EXE to self-register itself for automatically being started when the user logs in. This function calls GetModuleFileName and then invokes another helper function called RegisterMyProgramForStartup that does the writing to the registry.
Call IsMyProgramRegisteredForStartup(L"My_Program") to detect if the registration actually exists and appears valid.
One quick note. The performance impact of checking to see if the key exists before actually writing it out again is negligible. You could just call RegisterProgram blindly and it will overwrite the key if it already exists. Detecting if the registration exists is useful for initializing your UI checkbox that enables or disables auto-start. (You are giving your users a choice, right? Because I hate apps that automatically install themselves to run automatically without giving me a choice.)
BOOL IsMyProgramRegisteredForStartup(PCWSTR pszAppName)
{
HKEY hKey = NULL;
LONG lResult = 0;
BOOL fSuccess = TRUE;
DWORD dwRegType = REG_SZ;
wchar_t szPathToExe[MAX_PATH] = {};
DWORD dwSize = sizeof(szPathToExe);
lResult = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_READ, &hKey);
fSuccess = (lResult == 0);
if (fSuccess)
{
lResult = RegGetValueW(hKey, NULL, pszAppName, RRF_RT_REG_SZ, &dwRegType, szPathToExe, &dwSize);
fSuccess = (lResult == 0);
}
if (fSuccess)
{
fSuccess = (wcslen(szPathToExe) > 0) ? TRUE : FALSE;
}
if (hKey != NULL)
{
RegCloseKey(hKey);
hKey = NULL;
}
return fSuccess;
}
BOOL RegisterMyProgramForStartup(PCWSTR pszAppName, PCWSTR pathToExe, PCWSTR args)
{
HKEY hKey = NULL;
LONG lResult = 0;
BOOL fSuccess = TRUE;
DWORD dwSize;
const size_t count = MAX_PATH*2;
wchar_t szValue[count] = {};
wcscpy_s(szValue, count, L"\"");
wcscat_s(szValue, count, pathToExe);
wcscat_s(szValue, count, L"\" ");
if (args != NULL)
{
// caller should make sure "args" is quoted if any single argument has a space
// e.g. (L"-name \"Mark Voidale\"");
wcscat_s(szValue, count, args);
}
lResult = RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, NULL, 0, (KEY_WRITE | KEY_READ), NULL, &hKey, NULL);
fSuccess = (lResult == 0);
if (fSuccess)
{
dwSize = (wcslen(szValue)+1)*2;
lResult = RegSetValueExW(hKey, pszAppName, 0, REG_SZ, (BYTE*)szValue, dwSize);
fSuccess = (lResult == 0);
}
if (hKey != NULL)
{
RegCloseKey(hKey);
hKey = NULL;
}
return fSuccess;
}
void RegisterProgram()
{
wchar_t szPathToExe[MAX_PATH];
GetModuleFileNameW(NULL, szPathToExe, MAX_PATH);
RegisterMyProgramForStartup(L"My_Program", szPathToExe, L"-foobar");
}
int _tmain(int argc, _TCHAR* argv[])
{
RegisterProgram();
IsMyProgramRegisteredForStartup(L"My_Program");
return 0;
}
To check whether or not the value exists, call RegQueryValueEx.
LONG retval = RegQueryValueEx(hKey, "myprogram", NULL, NULL, NULL, NULL);
Note that what you called newValue is actually a key rather than a value. To avoid confusion you should name it such. I used the name hKey.
Then to check whether or not the value exists, compare retval against ERROR_SUCCESS as described in the documentation.
The other problem with your code is that there is absolutely no error checking. I'll leave that to you to address.
You forget to write an argument about security access
I am maintaining an application that uses SetupDiGetDeviceInterfaceDetail() to find out information on the installed serial ports on the computer. I have noticed while testing this that there are some devices, such as my Lucent WinModem, that do not show up in that enumeration. It turns out that I am having a similar issue with a set of devices manufactured by my company that implement the serial port interface. My assumption is that there is something that is missing from the INF file for the device. Does anyone know what kinds of conditions can result in this kind of omission?
Edit: Here is a sample of the code that I am using to enumerate the serial ports. I have tried various combinations of flags but have not seen any significant difference in behaviour.
DEFINE_GUID(GUID_CLASS_COMPORT, 0x4d36e978, 0xe325, 0x11ce, 0xbf, 0xc1, \
0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18);
GUID *serial_port_guid = const_cast<GUID *>(&GUID_CLASS_COMPORT);
HDEVINFO device_info = INVALID_HANDLE_VALUE;
SP_DEVICE_INTERFACE_DETAIL_DATA *detail_data = 0;
device_info = SetupDiGetClassDevs(
serial_port_guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if(device_info != INVALID_HANDLE_VALUE)
{
uint4 const detail_data_size = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + 256;
detail_data = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA *>(new char[detail_data_size]);
SP_DEVICE_INTERFACE_DATA ifc_data;
bool more_interfaces = true;
int rcd;
memset(&ifc_data, 0, sizeof(ifc_data));
memset(detail_data, 0, detail_data_size);
ifc_data.cbSize = sizeof(ifc_data);
detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
for(uint4 index = 0; more_interfaces; ++index)
{
rcd = SetupDiEnumDeviceInterfaces(device_info, 0, serial_port_guid, index, &ifc_data);
if(rcd)
{
// we need to get the details of this device
SP_DEVINFO_DATA device_data = { sizeof(SP_DEVINFO_DATA) };
rcd = SetupDiGetDeviceInterfaceDetail(
device_info, &ifc_data, detail_data, detail_data_size, 0, &device_data);
if(rcd)
{
StrAsc device_path(detail_data->DevicePath);
byte friendly_name[256];
rcd = SetupDiGetDeviceRegistryProperty(
device_info, &device_data, SPDRP_FRIENDLYNAME, 0, friendly_name, sizeof(friendly_name), 0);
if(rcd)
{
std::for_each(
port_names.begin(),
port_names.end(),
update_friendly_name(
reinterpret_cast<char const *>(friendly_name)));
}
}
else
more_interfaces = false;
}
}
}
This is more of a question about the issue. When you call the function the first arg you pass in should be DeviceInfoSet which you likely got from the SetupDiGetClassDevs function. When you called the SetupDiGetClassDevs function what did you specify for the flags (Last Argument) Quoting Microsoft's Page on the function:
DIGCF_ALLCLASSES
Return a list of installed devices for all device setup classes or all
device interface classes.
DIGCF_DEVICEINTERFACE
Return devices that support device interfaces for the specified device
interface classes. This flag must be
set in the Flags parameter if the
Enumerator parameter specifies a
device instance ID.
DIGCF_DEFAULT
Return only the device that is associated with the system default
device interface, if one is set, for
the specified device interface
classes.
DIGCF_PRESENT
Return only devices that are currently present in a system.
DIGCF_PROFILE
Return only devices that are a part of the current hardware profile.
Depending on your choice the list of devices changes. For example The Present flag will only show devices plugged in actively.
UPDATE: Thanks for the sample code.
My question now, is if you want to know the friendly name of the modem why not use the same call but specify the Modem Guid instead of the COM Port? I have the Modem GUID being 4D36E96D-E325-11CE-BFC1-08002BE10318
In the registry I can see a value called 'AttachedTo' which specifies a COM Port. I'll have to research which property thats is tied to in the API. The registry key is at
HKLM\SYSTEM\CurrentControlSet\Control\Class{4D36E96D-E325-11CE-BFC1-08002BE10318}\
ANOTHER UPDATE:
Looking closer at the sample code. Based on this, if you are trying to get the device interface class that should return a SP_DEVICE_INTERFACE_DETAIL_DATA Structure. That wouldn't provide a way of getting the friendly name of the device. I believe instead you would want the device instance.
From what I've read, the Device Interface is used to as a way to get the device path which can be used to write to it.
One thing I did to test your code was try it again the Disk Device Interface. I made a few changes to get it to work on my system and it still isn't quite done. I think the one problem (probably more) is that I need to resize the DevicePath variable inbetween the SetupDiGetDeviceInterfaceDetail calls.
void Test()
{
GUID *serial_port_guid = const_cast<GUID *>(&GUID_DEVINTERFACE_DISK);
HDEVINFO device_info = INVALID_HANDLE_VALUE;
SP_DEVICE_INTERFACE_DETAIL_DATA detail_data;
device_info = SetupDiGetClassDevs(
serial_port_guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if(device_info != INVALID_HANDLE_VALUE)
{
//uint4 const detail_data_size = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);// + 256;
//detail_data = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA *>(new char[detail_data_size]);
SP_DEVICE_INTERFACE_DATA ifc_data;
bool more_interfaces = true;
int rcd;
memset(&ifc_data, 0, sizeof(ifc_data));
//memset(detail_data, 0, detail_data_size);
ifc_data.cbSize = sizeof(ifc_data);
detail_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
for(uint4 index = 0; more_interfaces; ++index)
{
rcd = SetupDiEnumDeviceInterfaces(device_info, 0, serial_port_guid, index, &ifc_data);
if(rcd)
{
// we need to get the details of this device
SP_DEVINFO_DATA device_data;
device_data.cbSize = sizeof(SP_DEVINFO_DATA);
DWORD intReqSize;
rcd = SetupDiGetDeviceInterfaceDetail(device_info, &ifc_data, 0, 0, &intReqSize, &device_data);
rcd = SetupDiGetDeviceInterfaceDetail(device_info, &ifc_data, &detail_data,intReqSize,&intReqSize,&device_data);
if(rcd)
{
//StrAsc device_path(detail_data->DevicePath);
byte friendly_name[256];
rcd = SetupDiGetDeviceRegistryProperty(
device_info, &device_data, SPDRP_FRIENDLYNAME, 0, friendly_name, sizeof(friendly_name), reinterpret_cast<DWORD *>(sizeof(friendly_name)));
if(rcd)
{
cout<<reinterpret_cast<char const *>(friendly_name);
}
else
{ int num = GetLastError();
}
}
else
{
int num = GetLastError();
}
}
else
more_interfaces = false;
}
}
SetupDiDestroyDeviceInfoList(device_info);
}
Also, in the INF, you may have to add the AddInterface directive to associate your driver with the correct interface.
I am not sure whether following hotfix will solve your problem as mentioned in
http://support.microsoft.com/kb/327868
One more intersting point: GUID_CLASS_COMPORT is obsolete from Win2000 onwards..
http://msdn.microsoft.com/en-us/library/bb663140.aspx
http://msdn.microsoft.com/en-us/library/bb663174.aspx
Another site I find having 9 different ways of enumeration. Best of luck.
http://www.naughter.com/enumser.html
You say your device is present and accessible, but are you accessing your device directly or are you accessing a port by name and number COMn:
I have a WinModem that is connected to my audio driver. I have no serial port, not even a simulated one.
I decided to punt on this and to do away with the dependency on the SetupDi() functions. Instead, I have written code that traverses the subkeys in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum to find any drivers that support the serial port GUID. I have the feeling that this is what the device manager does. In case anyone is interested, my code fragment can be seen below:
typedef std::string StrAsc;
typedef std::pair<StrAsc, StrAsc> port_name_type;
typedef std::list<port_name_type> friendly_names_type;
void SerialPortBase::list_ports_friendly(friendly_names_type &port_names)
{
// we will first get the list of names. This will ensure that, at the very least, we get
// the same list of names as we would have otherwise obtained.
port_names_type simple_list;
list_ports(simple_list);
port_names.clear();
for(port_names_type::iterator pi = simple_list.begin(); pi != simple_list.end(); ++pi)
port_names.push_back(friendly_name_type(*pi, *pi));
// we will now need to enumerate the subkeys of the Enum registry key. We will need to
// consider many levels of the registry key structure in doing this so we will use a list
// of key handles as a stack.
HKEY enum_key ;
char const enum_key_name[] = "SYSTEM\\CurrentControlSet\\Enum";
StrAsc const com_port_guid("{4d36e978-e325-11ce-bfc1-08002be10318}");
char const class_guid_name[] = "ClassGUID";
char const friendly_name_name[] = "FriendlyName";
char const device_parameters_name[] = "Device Parameters";
char const port_name_name[] = "PortName";
long rcd = ::RegOpenKeyEx(
HKEY_LOCAL_MACHINE, enum_key_name, 0, KEY_READ, &enum_key);
char value_buff[MAX_PATH];
StrAsc port_name, friendly_name;
if(!port_names.empty() && rcd == ERROR_SUCCESS)
{
std::list<HKEY> key_stack;
key_stack.push_back(enum_key);
while(!key_stack.empty())
{
// we need to determine whether this key has a "ClassGUID" value
HKEY current = key_stack.front();
uint4 value_buff_len = sizeof(value_buff);
key_stack.pop_front();
rcd = ::RegQueryValueEx(
current,
class_guid_name,
0,
0,
reinterpret_cast<byte *>(value_buff),
&value_buff_len);
if(rcd == ERROR_SUCCESS)
{
// we will only consider devices that match the com port GUID
if(com_port_guid == value_buff)
{
// this key appears to identify a com port. We will need to get the friendly name
// and try to get the 'PortName' from the 'Device Parameters' subkey. Once we
// have those things, we can update the friendly name in our original list
value_buff_len = sizeof(value_buff);
rcd = ::RegQueryValueEx(
current,
friendly_name_name,
0,
0,
reinterpret_cast<byte *>(value_buff),
&value_buff_len);
if(rcd == ERROR_SUCCESS)
{
HKEY device_parameters_key;
rcd = ::RegOpenKeyEx(
current,
device_parameters_name,
0,
KEY_READ,
&device_parameters_key);
if(rcd == ERROR_SUCCESS)
{
friendly_name = value_buff;
value_buff_len = sizeof(value_buff);
rcd = ::RegQueryValueEx(
device_parameters_key,
port_name_name,
0,
0,
reinterpret_cast<byte *>(value_buff),
&value_buff_len);
if(rcd == ERROR_SUCCESS)
{
friendly_names_type::iterator fi;
port_name = value_buff;
fi = std::find_if(
port_names.begin(), port_names.end(), port_has_name(port_name));
if(fi != port_names.end())
fi->second = friendly_name;
}
::RegCloseKey(device_parameters_key);
}
}
}
}
else
{
// since this key did not have what we expected, we will need to check its
// children
uint4 index = 0;
rcd = ERROR_SUCCESS;
while(rcd == ERROR_SUCCESS)
{
value_buff_len = sizeof(value_buff);
rcd = ::RegEnumKeyEx(
current, index, value_buff, &value_buff_len, 0, 0, 0, 0);
if(rcd == ERROR_SUCCESS)
{
HKEY child;
rcd = ::RegOpenKeyEx(current, value_buff, 0, KEY_READ, &child);
if(rcd == ERROR_SUCCESS)
key_stack.push_back(child);
}
++index;
}
}
::RegCloseKey(current);
}
}
} // list_ports_friendly