I have a C++ Custom Action Project. I have two functions, RegProductName and GetProductName.
I call RegProductName and it has three possible outcomes. I have these in an if statement that if it is outcome 1 or outcome 2 then i call my second function GetProductName but i can't seem to get it working. Can anyone give me an example of calling one function from another please?
extern "C" UINT __stdcall RegProductName(MSIHANDLE hInstall)
{
AssertSz(FALSE, "debug here");
DebugBreak();
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
char szProductName[MAX_PATH];
hr = WcaInitialize(hInstall, "RegProductName");
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
strcpy(szProductName, Orc_Get_Product_Name());
if(szProductName == "ORCHESTRATOR")
{
GetProductName();
}
else if (szProductName == "CORAL")
{
GetProductName();
}
else
{
MsiSetProperty(hInstall, "PRODUCTNAME", szProductName);
}
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
The error is "Too few arguments in function call when i hover over GetProductName();
extern "C" UINT __stdcall GetProductName(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
DWORD Ret;
CHAR *Section = "General";
CHAR szBuffer[MAX_PATH];
CHAR szProductIniFile[MAX_PATH];
char lpszString[MAX_PATH];
int lplValue;
hr = WcaInitialize(hInstall, "GetProductName");
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
TCHAR* szValueBuf = NULL;
DWORD cchValueBuf = 0;
UINT uiStat = MsiGetProperty(hInstall, TEXT("TEMPFOLDER"), TEXT(""), &cchValueBuf);
if (ERROR_MORE_DATA == uiStat)
{
++cchValueBuf;
szValueBuf = new TCHAR[cchValueBuf];
if (szValueBuf)
{
uiStat = MsiGetProperty(hInstall, TEXT("TEMPFOLDER"), szValueBuf, &cchValueBuf);
}
}
if (ERROR_SUCCESS != uiStat)
{
if (szValueBuf != NULL)
delete[] szValueBuf;
return ERROR_INSTALL_FAILURE;
}
strcpy(szProductIniFile,szValueBuf);
Ret = strlen(szProductIniFile);
if(szProductIniFile[Ret-1] != '\\')
strcat(szProductIniFile,"\\");
strcat(szProductIniFile, "Product.ini");
Ret = GetPrivateProfileString(Section, // Section Title [General]
"PRODUCT_NAME", // Entry
"Orchestrator", // Default Value
szBuffer, // Address of buffer to read to
MAX_PATH, // Length of buffer
szProductIniFile); // .ini file name
if (strlen(szBuffer) == 0)
strcpy(szBuffer, "ORCHESTRATOR");
if (strlen(szBuffer) >= 3 && (stricmp(szBuffer+strlen(szBuffer)-3,"DEM") == 0))
lplValue = 1;
else
lplValue = 0;
MsiSetProperty(hInstall, "PRODUCTNAME", szBuffer);
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
Your GetProductName() function takes an argument MSIHANDLE hInstall. You'll need to provide that when calling it. For instance, if you just want to call it with the same handle as RegProductName() was called with:
GetProductName(hInstall);
This does not compare the string content:
if(szProductName == "ORCHESTRATOR")
either use strcmp() or use std::string and ==:
if(szProductName == std::string("ORCHESTRATOR"))
Your GetProductName looks like this:
extern "C" UINT __stdcall GetProductName(MSIHANDLE hInstall)
\________________/
The Argument
So it needs to take 1 argument, while you are calling it without argumanes at all:
getProductName( );
^
|
nothing is being passed here
hence the error you're getting. Based on your code you should probably pass your hInstall there:
getProductName( hInstall );
The GetProductName requires one argument of type MSIHANDLE whereas you are calling it without any parameter. Try instead
GetProductName(hInstall);
GetProductName is defined as GetProductName(MSIHANDLE hInstall), that means you MUST pass relevant MSIHANDLE as parameter. And that's exactly the error you're getting.
But you're doing szProductName == "ORCHESTRATOR" - this is not how you compare strings in C. You seem to lack basic knowledge about C. You should not be writing in C or C++.
Related
I'm working with the following piece of code. DLLName is of type wchar_t*, and it's being set early on in my program. Before i reach this point in my code, DLLName is a valid path to a DLL, like L"C:\\Windows\\System32\\advapi32.dll"
wprintf(L"Location: %s\n", DLLName);
HMODULE hDLL = LoadLibraryW(DLLName);
What happens when my code reaches wprintf? The value of DLLName is not printed. In fact, DLLName is now a blank string, L""! Which causes the call to LoadLibraryW() to fail.
Weird. I comment out wprintf. When the debugger reaches the LoadLibraryW(), DLLName is the correct wide string with the path to my DLL. After LoadLibraryW(), the value of DLLName is L"\x4", and the call failed.
What's going on here? I am clueless on how to debug this.
EDIT: All of my code
BOOL FindOriginalCOMServer(wchar_t* GUID, wchar_t** DLLName)
{
HKEY hKey;
HKEY hCLSIDKey;
wchar_t name[MAX_PATH];
DWORD nameLength = MAX_PATH;
wprintf(L"[*] Beginning search for GUID %s\n", GUID);
LONG lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)L"SOFTWARE\\Classes\\CLSID", 0, KEY_READ, &hKey);
if (lResult != ERROR_SUCCESS) {
wprintf(L"[-] Error getting CLSID path\n");
return FALSE;
}
// Make sure HKLM\Software\Classes\CLSID\{GUID} exists
lResult = RegOpenKeyExW(hKey, GUID, 0, KEY_READ, &hCLSIDKey);
if (lResult != ERROR_SUCCESS) {
wprintf(L"[-] Error getting GUID path\n");
RegCloseKey(hKey);
return FALSE;
}
// Read the value of HKLM's InProcServer32
lResult = RegGetValueW(hCLSIDKey, (LPCWSTR)L"InProcServer32", NULL, RRF_RT_ANY, NULL, (PVOID)&name, &nameLength);
if (lResult != ERROR_SUCCESS) {
wprintf(L"[-] Error getting InProcServer32 value: %d\n", lResult);
RegCloseKey(hKey);
RegCloseKey(hCLSIDKey);
return FALSE;
}
*DLLName = name;
return TRUE;
}
Then:
wchar_t* DLLName = new wchar_t[MAX_PATH];
if (!FindOriginalCOMServer((wchar_t*)lplpsz, &DLLName))
{
wprintf(L"[-] Couldn't find original COM server\n");
return S_FALSE;
}
wprintf("[+] Found original COM server: %s\n", DLLName);
HMODULE hDLL = LoadLibraryW(DLLName);
DLLName will point to a local char array in FindOriginalCOMServer, which will no longer exist once that function returns.
You should pass DLLName to FindOriginalCOMServer() as a wchar_t* (one pointer, not two) then get rid of name and work with DLLName directly. Or, you could use wcscpy_s() to copy the string from name to DLLName.
So I am learning some dll injection stuff on a test executable. Once I have injected my dll, I try to get the module handle and with that I try to get the base address of the module(the main exe).
DWORD dwGetModuleBaseAddress(DWORD dwProcessIdentifier, WCHAR *lpszModuleName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessIdentifier);
DWORD dwModuleBaseAddress = 0;
if(hSnapshot != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 ModuleEntry32 = {0};
ModuleEntry32.dwSize = sizeof(MODULEENTRY32);
if(Module32First(hSnapshot, &ModuleEntry32))
{
do
{
if(wcscmp(ModuleEntry32.szModule, lpszModuleName) == 0)
{
dwModuleBaseAddress = (DWORD)ModuleEntry32.modBaseAddr;
break;
}
}
while(Module32Next(hSnapshot, &ModuleEntry32));
}
CloseHandle(hSnapshot);
}
return dwModuleBaseAddress;
}
That is how I try to do it. This is after my dll is injected, but it seems to return INVALID_HANDLE_VALUE for some reason. I found the function from one website and modified it a bit but it still doesn't seem to work. If you have a more cleaner method to get the base address I would be glad to know about it.
Edit the problem is now with this line:
if(wcscmp(ModuleEntry32.szModule, lpszModuleName) == 0)
It never is 0 but there is a module name I am looking for, I can see my exe in the debugger but this comparison doesn't somehow work.
This is how I call the function
HWND window = FindWindow(0, LPCWSTR("test"));
DWORD pID = 0;
GetWindowThreadProcessId(window, &pID);
base = dwGetModuleBaseAddress(pID, (WCHAR*)("test"));
You are using functions designed for wide character strings.
"test"
L"test"
The top line is a regular c string char array. The second line is using the L macro to define the string literal as a wide char c string.
Casting to WCHAR* will not solve the issue, you either have to define them using the L macro or switch your functions to use regular char arrays.
Here is a regular char array version of your function I use
char* GetModuleBaseAddress(const char* modName, DWORD procId)
{
char* modBaseAddr{ nullptr };
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
if (hSnap != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 modEntry{};
modEntry.dwSize = sizeof(modEntry);
if (Module32First(hSnap, &modEntry))
{
do
{
if (!_stricmp(modEntry.szModule, modName))
{
modBaseAddr = (char*)modEntry.modBaseAddr;
break;
}
} while (Module32Next(hSnap, &modEntry));
}
}
CloseHandle(hSnap);
return modBaseAddr;
}
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'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 attempting to list the device information for all the monitors currently connected to the computer. I have a function that can do this and its 90% done except when I go to call the function SetupDiGetClassDevs() with the 2nd parameter set(not NULL) then the function always fails(returns INVALID_HANDLE_VALUE).
When I call GetLastError() I get the error 13(decimal), ie, "The data is invalid" which I am not sure what that means?
What is going wrong? Can you provide any advice on whats happening and how I can fix it?
Function Information:
HDEVINFO SetupDiGetClassDevs(
_In_opt_ const GUID *ClassGuid,
_In_opt_ PCTSTR Enumerator, // According to MSDN this param MUST be set if I want Device Information for a specific class(Monitors)
_In_opt_ HWND hwndParent,
_In_ DWORD Flags
);
My function that attempts to get a Device Information Set for Monitors only and output each monitors details(the error line is commented):
void printDeviceData(GUID guID)
{
// Device Classes: http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426
// System Device Classes: http://msdn.microsoft.com/en-us/library/windows/hardware/ff553428
// Monitor Class GUI: {4d36e96e-e325-11ce-bfc1-08002be10318}
DWORD dataT = 0;
PCTSTR monitorGuID = _T("");
SP_DEVINFO_DATA deviceInfoData = {0};
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
deviceInfoData.ClassGuid = guID;
// Step 1: Get Device Information Set for Monitors only
// ERROR OCCURS HERE: SetupDiGetClassDevs() always fails
// Also tried these values for param 2: "Monitor" "PCI" but all cause the function to return INVALID_HANDLE_VALUE
HDEVINFO hDevInfo = SetupDiGetClassDevs(&guID, _T("{4d36e96e-e325-11ce-bfc1-08002be10318}"), NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) {
//outputLastError(_T("Fail 1"));
printf("hDevInfo == INVALID_HANDLE_VALUE\n");
return;
}
else printf("SUCCESS 1\n");
if (SetupDiGetSelectedDevice(hDevInfo, &deviceInfoData) == FALSE) {
//outputLastError(_T("SetupDiGetSelectedDevice(hDevInfo, &deviceInfoData) == FALSE"));
printf("SetupDiGetSelectedDevice(hDevInfo, &deviceInfoData) == FALSE, %d, %x\n", GetLastError(), GetLastError());
return;
}
else printf("SUCCESS 2\n");
// Step 2: For each Monitor: Output Device information
const unsigned int FLAG_NUM = 30;
DWORD flags[] = {SPDRP_FRIENDLYNAME, SPDRP_ENUMERATOR_NAME, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, SPDRP_DEVICEDESC,
SPDRP_ADDRESS, SPDRP_BUSNUMBER, SPDRP_BUSTYPEGUID, SPDRP_CHARACTERISTICS, SPDRP_CLASS, SPDRP_CLASSGUID,
SPDRP_COMPATIBLEIDS, SPDRP_CONFIGFLAGS, SPDRP_DEVICE_POWER_DATA, SPDRP_DEVTYPE, SPDRP_DRIVER,
SPDRP_ENUMERATOR_NAME, SPDRP_EXCLUSIVE, SPDRP_HARDWAREID, SPDRP_INSTALL_STATE, SPDRP_LEGACYBUSTYPE,
SPDRP_LOCATION_INFORMATION, SPDRP_LOCATION_PATHS, SPDRP_LOWERFILTERS, SPDRP_MFG,
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, SPDRP_UI_NUMBER, SPDRP_UI_NUMBER_DESC_FORMAT, SPDRP_UPPERFILTERS,
SPDRP_SECURITY_SDS, SPDRP_SECURITY, SPDRP_SERVICE };
for (int i=0; i<=FLAG_NUM; i++) {
DWORD buffersize = 0;
LPTSTR buffer = NULL;
while (!SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, flags[i], &dataT,
(PBYTE)buffer, buffersize, &buffersize))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
// Change the buffer size.
if (buffer)
LocalFree(buffer);
buffer = (LPTSTR)LocalAlloc(LPTR, buffersize);
}
else {
// Insert error handling here.
break;
}
}
printf("Data: %d: %s\n", i, buffer);
if (buffer)
LocalFree(buffer);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
According to the documentation, Enumerator must be set to a valid device Instance ID which according to http://msdn.microsoft.com/en-us/library/windows/hardware/ff541327 has to be specified like this
"PCI\VEN_1000&DEV_0001&SUBSYS_00000000&REV_02\1&08"
I haven't tested it, but I'd assume that's where the invalid data come from.