How to start an external program from service [duplicate] - c++

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.

Related

wxWidgets iterate through registry entries

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;
}

Error with function call CRegKey::QueryStringValue

Today i found this strange error in querying the time zone information .
The code to read the display name of time zones looks like this
typedef struct {
LONG Bias;
LONG StandardBias;
LONG DaylightBias;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
} TZI, * PTZI;
CRegKey RegKey;
CString regKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
int idx = 0;
bool bMoreKeys = true;
bool bSuccess = true;
while (bMoreKeys && bSuccess) {
CString sSubkeyName;
DWORD nLength = 200;
LPTSTR sBuffer = sSubkeyName.GetBufferSetLength(nLength);
FILETIME ftLastWriteTime;
bMoreKeys = (RegKey.EnumKey(idx, sBuffer, & nLength, & ftLastWriteTime) == ERROR_SUCCESS);
sSubkeyName.ReleaseBuffer();
if (!bMoreKeys) {
bMoreKeys = false;
break;
}
CString sSubKeyPath = regKey + _T("\\") + sSubkeyName;
CRegKey subKey;
if (subKey.Open(HKEY_LOCAL_MACHINE, sSubKeyPath, KEY_READ) != ERROR_SUCCESS) {
//LOG_ERROR
bSuccess = false;
break;
}
// Get the display name
CString sDispName;
DWORD nDispBufferLength = 1000;
LPTSTR sDispBuffer = sDispName.GetBufferSetLength(nDispBufferLength);
if (subKey.QueryStringValue(_T("Display"), sDispBuffer, & nDispBufferLength) != ERROR_SUCCESS) {
//LOG_ERROR
bSuccess = false;
break;
}
sDispName.ReleaseBuffer();
// Get the Bias (for later sorting);
TZI tzi;
nLength = sizeof(tzi);
if (subKey.QueryBinaryValue(_T("TZI"), & tzi, & nLength) != ERROR_SUCCESS && nLength != sizeof(tzi)) {
//LOG_ERROR
bSuccess = false;
}
(void) subKey.Close();
idx++;
}
But for some of the time zones for eg : argentina the return value is not error_success.
On further debugging into the QueryStringValue , i found this
if ((nBytes % sizeof(TCHAR) != 0) || (pszValue[nBytes / sizeof(TCHAR) - 1] != 0)) {
return ERROR_INVALID_DATA;
}
and when the nBytes size is 48 for any time zone display value then always error_invalid_data is returned .
To confirm this i have changed the regkey api call to
DWORD dwType = 0;
ULONG nBytes = 256 * sizeof(TCHAR);
TCHAR displayValue[256];
if (subKey.QueryValue(
_T("Display"), & dwType, (LPBYTE) displayValue, & nBytes) == 0 && dwType == REG_SZ) {}
and i dont get the error anymore and everything is working fine .
Couldnt find out any valid reason that why it occurs.Anyone has better explanation why for all the timezones with size 48 we get the invalid data error .
Thanks in advance.
EDIT:
PS: In some machines everything is working well with the code mentioned above (i.e the first one) and in some other machines its not which is what really strange and with the second code everywhere i could see the code is working fine.
The code
CRegKey Key;
LONG nA = Key.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\Argentina Standard Time"), KEY_READ);
TCHAR pszValue[24];
ULONG nValueLength = _countof(pszValue);
LONG nB = Key.QueryStringValue(_T("Display"), pszValue, &nValueLength);
gets you a different error, 234, which is ERROR_MORE_DATA "More data is available."
This happens because the value itself does not fit fully into the provided buffer.
This is what you want instead:
CRegKey Key;
LONG nA = Key.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\Argentina Standard Time"), KEY_READ);
ULONG nValueLength = 0;
LONG nB = Key.QueryStringValue(_T("Display"), NULL, &nValueLength);
CString sValue;
if(nValueLength > 0)
{
LONG nC = Key.QueryStringValue(_T("Display"), sValue.GetBufferSetLength(nValueLength - 1), &nValueLength);
}
Or, you need to provide large enough buffer, which you assume to be sufficient.
The string in the reg may not be null-terminated. Hence the following validation fails:
if ((nBytes % sizeof(TCHAR) != 0) || (pszValue[nBytes / sizeof(TCHAR) -1] != 0))
{
return ERROR_INVALID_DATA;
}
Note that it seems to be a bug in CRegKey::QueryStringValue, RegQueryValueEx allows to read not-null-terminated strings, and it is callers responsibility to handle this case. I had come across this exact issue in the code and had to replace the call to CRegKey::QueryStringValue with my own function.

Reading Windows Registry Key REG_BINARY in c++

I'm trying to read a registry key in c++,
that's my function:
DWORD regkey()
{
HKEY hKey;
DWORD dwDisp = REG_BINARY;
DWORD dwSize = sizeof(dwDisp);
DWORD dwValue = 0;
DWORD dwReturn;
DWORD dwBufSize = sizeof(dwDisp);
if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HERE\\IS\\THE\\REGKEY",0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
{
DWORD error = RegQueryValueEx(hKey,L"key",0,0, (LPBYTE)&dwReturn, &dwBufSize);
if(error == ERROR_SUCCESS)
{
return dwReturn;
}
}
RegCloseKey(hKey);
return 0;
}
but it's returning nothing... please help me.
The registry functions will return a meaningful error code, and that can help you diagnose the problem. Try holding on to that code:
{
HKEY hKey;
DWORD dwDisp = REG_BINARY;
DWORD dwSize = sizeof(dwDisp);
DWORD dwValue = 0;
DWORD dwReturn;
DWORD dwBufSize = sizeof(dwReturn);
DWORD dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HERE\\IS\\THE\\REGKEY",0, KEY_READ, &hKey) ;
if( dwError == ERROR_SUCCESS)
{
dwError = RegQueryValueEx(hKey,L"key",0,0, (LPBYTE)&dwReturn, &dwBufSize);
if(error == ERROR_SUCCESS)
{
// it worked!
}
else
{
// it failed to read, check dwError for the error code
dwResult = 0;
}
RegCloseKey(hKey);
}
else
{
// it failed to open, check dwError for the error code
dwResult = 0;
}
return 0;
}
If you're using Visual Studio, you can break on any of the failure points and evaluate dwError,hr in your watch window. The ,hr format specifier causes the debugger to look up the error code for you and present a meaningful string that describes the problem. That should lead you to an understanding of what went wrong.
If you can tell us which function is failing and which code you're getting back from that function, we might be able to provide more detailed help. As it stands now, you've presented us with a bit of a guessing game. Maybe you've misspelled your registry key name or given an incorrect path. Your code seems to imply you're passing the registry key RegQueryValueEx(), but you're meant to pass a value name, not a key name, to that function. Maybe you have a problem with access privileges because you're looking at a protected part of the registry and not running as an account with enough rights to read that key. (And so, you should pass KEY_READ instead of KEY_ALL_ACCESS.)

Getting a proper value from RegQueryValueEx

I am trying to extract a value from the windows registry of type REG_SZ, using RegQueryValueEx, I got the value except it was riddled with strange "\000" before each letter.To show you what I mean here are some images:
Value I want(It is a device name of a wireless adapter)
Value I got:
here is the code:
HKEY hlistkey = NULL;
HKEY hkey = NULL;
int dwIndex=0;
string devName = returndevName(); //return current selected device name using iphlpapi.h
WCHAR KeyNameBuf[512];
DWORD keyNameSizBuf = 512;
char buffer[512];
RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002bE10318}") ,0,KEY_READ, &hlistkey );
if(!hlistkey)
{
cout << "failed" << endl;
}
while(RegEnumKeyEx(hlistkey,dwIndex++,KeyNameBuf,&keyNameSizBuf,0,NULL,NULL,NULL) == ERROR_SUCCESS )
{
RegOpenKeyEx(hlistkey, KeyNameBuf, 0, KEY_READ | KEY_SET_VALUE, &hkey);
if(hkey)
{
keyNameSizBuf = 512;
if(RegQueryValueEx(hkey,TEXT("NetCfgInstanceId"), 0,NULL,(LPBYTE)buffer,&keyNameSizBuf ) == ERROR_SUCCESS )
{
if(strcmp(buffer,devName.c_str() ) ==0)
{
//set value here
}
}
RegCloseKey(hkey);
}
}
}
comparing buffer and devName would not be the same because of the extra null characters .If I cast buffer to a string I simply got a "{" which is the first value.I need to get the value of the devename in the registry before I can change the "NetworkAddress" in the registry.
Since you are using WCHAR, I assume you are compiling with Unicode support. If this is true, then also the buffer needs to be WCHAR.

Get file type from windows registry in c++

I am trying to display file type of given filename (based on extension) using AssocQueryKey() API function.
The problem is thar return wrong HKEY value sometimes. For example the following function works correctly on win 7 ultimate x64, but fails for some extensions like ".mp3" on my win xp x86 machine (other extensions works though).
Even when "succeeded" and returns S_OK, GetLastError() is 1008 ALWAYS after AssocQueryKey() call:
// Return STL string representation of file type from windows registry
stlstring GetFileTypeFromRegistry(const stlstring& m_filename)
{
CRegKey reg;
HKEY key = {0};
stlstring s;
//Get file extension
LPCTSTR fExt = PathFindExtension(m_filename.c_str());
if(AssocQueryKey(NULL, ASSOCKEY_CLASS, fExt, TEXT(""), &key) != S_OK)
DisplayError(_T("AssocQueryKey != S_OK"), GetLastError());
else
DisplayError(_T("AssocQueryKey == S_OK"), GetLastError());
if(reg.Open ( key, NULL, KEY_QUERY_VALUE) != ERROR_SUCCESS){
reg.Close();
DisplayError((LPTSTR)fExt);
return s;
}
//DWORD out = 0;
/*WCHAR *h = new WCHAR[1024];
ZeroMemory(h, sizeof(h));
AssocQueryStringByKey(0, ASSOCSTR_EXECUTABLE, HKEY_CLASSES_ROOT, NULL, h, &out);
//MessageBox(0,_T("gbtbb"),h,MB_OK);
delete[] h;*/
ULONG m_sz = 256;
//if( reg.QueryStringValue(NULL, NULL, &m_sz) == ERROR_SUCCESS){
TCHAR *m_regstring = new TCHAR[m_sz + 1];
if(reg.QueryStringValue(NULL, m_regstring, &m_sz) == ERROR_SUCCESS){
//DisplayError(_T(""));
s += m_regstring;
/*delete[] m_regstring; m_regstring = NULL;
reg.Close();
return s;*/
} else {
DisplayError(_T("CRegKey::QueryStringValue()"), GetLastError());
}
s += m_regstring;
delete[] m_regstring; m_regstring = NULL;
reg.Close();
return s;
/*}
reg.Close();
return s;*/
}
Any ideas on this ?? This function is from a DLL which is loaded by windows explorer, implementing IQueryInfo::GetInfoTip() if that matters.
You shouldn't use GetLastError for functions that return the error code directly. The MSDN page for AssocQueryKey says "Returns S_OK if successful, or a COM error value otherwise.", which means you already get the error code in the return value.
If you just want to get the file type information, there's a much simpler solution: SHGetFileInfo. It's really simple to use, like this:
SHFILEINFO shfi;
SHGetFileInfo(filename, 0, &shfi, sizeof(shfi), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES);
// shfi.szTypeName now contains the file type string of the given filename