Trying to create a loader for an executable, it fails to execute when the length of string supplied in the Parameters is more than some length. But the Parameters are completely read from the initialization file. It also crashes when the Parameters=Null.
typedef int (__cdecl *ExecMain_t)(HINSTANCE, HINSTANCE, LPSTR, int);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HMODULE Loader = LoadLibraryExA(".\\library.dll", NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
char* Parameters;
CIniFile iniReader(".\\Configure.ini");
Parameters = iniReader.IniReadValue("App", "Parameters");
char xCommand[MAX_PATH] = {0};
_snprintf_s(xCommand, _TRUNCATE, "-verify "); //parameter from exe
strcat_s(xCommand, _TRUNCATE, Parameters);
strcpy_s(lpCmdLine, _TRUNCATE, xCommand);
delete[] Parameters;
Parameters = NULL;
ExecMain_t procExecMain = (ExecMain_t)GetProcAddress(Loader,"ExecMain");
procExecMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
return 1;
}
Crash description:
Problem Event Name: APPCRASH
Application Name: exec.exe
Application Version: 0.0.0.0
Application Timestamp: 530df50a
Fault Module Name: KERNELBASE.dll
Fault Module Version: 6.2.9200.16384
EDIT: Trying to figure out why it crashes when the Parameter string length is increases.
Even crashes if I pass more characters along with "-verify " (passing parameter from exe)
EDIT2: Modified the code by replacing MAX_PATH by _TRUNCATE. Seems to work for some characters in the parameters but when exceeded, crashes.
Here are several problems:
char xCommand[MAX_PATH] = {0};
_snprintf_s(xCommand, _TRUNCATE, "-verify "); //parameter from exe
strcat_s(xCommand, _TRUNCATE, Parameters);
strcpy_s(lpCmdLine, _TRUNCATE, xCommand);
I think you should use strcpy_s instead of _snprintf_s, after all I don't see you printing any format in xCommand.
Then, the second parameter, _TRUNCATE should actually be MAX_PATH since the second parameter of this functions is the number of elements of the destination buffer.
But most important, the third line is completely wrong, because you're trying to write into the input buffer lpCmdLine and that will overwrite the memory beyond the buffer bounds. What you need is to copy the content of the original buffer into your new command buffer. Something like this (notice I wrote this code in the browser so it may contain errors):
char xCommand[MAX_PATH] = {0};
strcpy_s(xCommand, MAX_PATH, lpCmdLine); // copy the original command line
strcat_s(xCommand, MAX_PATH, " -verify "); // append
strcat_s(xCommand, MAX_PATH, Parameters); // append
and then pass this buffer to your ExecMain function:
procExecMain(hInstance, hPrevInstance, xCommand, nCmdShow);
Related
I'm trying to convert a const wchar_t* to LPWSTR but I'm getting the error E0513.
I'm using Visual Studio with C++17.
Here is my code:
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
LPWSTR* argv;
int argCount;
argv = CommandLineToArgvW(GetCommandLineW(), &argCount);
if (argv[1] == nullptr) argv[1] = L"2048"; <-- conversion error
}
How to fix this?
To answer your question:
You can use const_cast:
argv[1] = const_cast<LPWSTR>(L"2048");
Or a local wchar_t[] array:
wchar_t arg[] = L"2048";
argv[1] = arg;
However, CommandLineToArgvW() will never return any array elements set to nullptr to begin with. All array elements are null-terminated string pointers, so empty parameters would have to be specified as quoted strings ("") on the command-line in order to be parsed, and as such will be returned as 0-length string pointers in the array. So, you would need to check for that condition instead, eg:
if (argCount > 1 && *(argv[1]) == L'\0')
For Visual Studio:
C/C++ => Language => Compatibility Mode => Set to NO.
This will allow to do such casting.
I'm trying to write a program in C++ that launches an executable. When I try to run the program, nothing happens. But when I try to run the program on the same directory and drive the exe is in, it does work.
Here's the code:
else if (result1 == 0) {
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
{
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(si);
CreateProcess(L"D:\\Games\\Origin Games\\Command and Conquer Red Alert\\RA95Launcher.exe", NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
When I do it like this:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
{
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(si);
CreateProcess(L"D:\\Games\\Origin Games\\Command and Conquer Red Alert\\RA95Launcher.exe", NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, "D:\\Games\\Origin Games\\Command and Conquer Red Alert\\", &si, &pi);
I get the error:
argument of type "const char *" is incompatible with parameter of type "LPCWSTR"
Any help would be appreciated.
Most likely you should set the lpCurrentDirectory parameter to the directory the exe is in.
See: CreateProcess
It's often that executables like RA95Launcher.exe are depending on other files. Usually these are reffered to by a relative path from perspective of the Current Working Directory.
If you launch the process from within the directory the exe is in, your automatically in the correct Current Working Directory. If you have the launcher running at a different location, you'll need to specify it.
I get the error: argument of type "const char *" is incompatible with
parameter of type "LPCWSTR"
This means that you're trying to pass incompatible type to function. LPCWSTR is Windows.h typedef for const wchar_t*(a UTF-16 string), but CreateProcess requires ANSI string(LPCSTR). You can either call the appropriate function, in this case - CreateProcessW(W at the end of the function name means it takes UTF-16 strings as parameters) or make your string ANSI by removing L before it.
BOOL SetupGetInfDriverStoreLocation(
_In_ PCTSTR FileName,
_In_opt_ PSP_ALTPLATFORM_INFO AlternatePlatformInfo,
_In_opt_ PCTSTR LocaleName,
_Out_ PTSTR ReturnBuffer,
_In_ DWORD ReturnBufferSize,
_Out_opt_ PDWORD RequiredSize
);
How can I call this function if I have FileName as:
TCHAR FileName[MAX_VALUE_NAME];
where #define MAX_VALUE_NAME 16383
This is a very common pattern in the Windows API; you give the function a buffer and the size of the buffer (often as a character count) and the call will fail if the buffer is too small.
#define MAX_VALUE_NAME 16383
TCHAR fullpath[MAX_VALUE_NAME];
BOOL result = SetupGetInfDriverStoreLocation(TEXT("NameOfMyFile.inf"), 0, 0, fullpath, MAX_VALUE_NAME, 0);
if (result) MessageBox(0, fullpath, 0, 0);
Your buffer is very large (MSDN says "The maximum supported path size is MAX_PATH") so it should be able to hold any .inf path but the function is designed so you can first call it with a NULL buffer:
To determine the size of the return buffer that is required to contain
the fully qualified file name of the specified INF file in the driver
store, call SetupGetInfDriverStoreLocation and set ReturnBuffer to
NULL, ReturnBufferSize to zero, and supply RequiredSize.
SetupGetInfDriverStoreLocation will return the required buffer size in
RequiredSize.
I'd call this the "peek, allocate, get" pattern. If there is a chance the data might actually change then you should call it in a loop until the "get" call succeeds.
VS10, MBCS, Unicode preprocessor defs.
Having gone through Dr Google's casebook, the best help was here, but it doesn't quite address this particular problem. Consider this code where thisexePath is the path to the executable:
cmdLineArg = (wchar_t *)calloc(BIGGERTHANMAXPATH, sizeof(wchar_t));
wcscpy_s (cmdLineArg, maxPathFolder, L"\\\\??\\C:\\My directory\\My directory\\");
CreateProcessW (thisexePath, cmdLineArg, NULL, NULL, FALSE, NULL, NULL, NULL, &lpStartupInfo, &lpProcessInfo)
When the program is called
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
wchar_t hrtext[256];
swprintf_s(hrtext, sizeof(hrtext), L"%ls", lpCmdLine);
//deal with possible buffer overflow later
MessageBoxW(NULL, hrtext, L"Display", MB_ICONINFORMATION);
}
hrtext only displays "My directory\My directory\" Something obvious missed here?
This is not correct:
wchar_t hrtext[256];
swprintf_s(hrtext, sizeof(hrtext), L"%ls", lpCmdLine);
The second parameter should denote the number of characters, not bytes.
MSDN link to swprintf_s
It should be this:
wchar_t hrtext[256];
swprintf_s(hrtext, sizeof(hrtext) / sizeof(wchar_t), L"%ls", lpCmdLine);
or simply:
wchar_t hrtext[256];
swprintf_s(hrtext, 256, L"%ls", lpCmdLine);
I'm making a C++ program using Visual C++ 2008 Express that gets the paths to specific apps from the registry, display a list of those that are installed, allows the user to pick one to configure, then launch the selected app.
This program is supposed to retrieve the paths to (currently) three apps by using RegGetValue (from windows.h).
While it works with Apps n°1 and 3, it fails with app n°2.
The part of the source that handles the registry is available on Pastebin: http://pastebin.com/9X2hjGqh.
I get error n°234 (ERROR_MORE_DATA) when I add a cout to get the function's return.
RegGetValue syntax:
LONG WINAPI RegGetValue(
_In_ HKEY hkey,
_In_opt_ LPCTSTR lpSubKey,
_In_opt_ LPCTSTR lpValue,
_In_opt_ DWORD dwFlags,
_Out_opt_ LPDWORD pdwType,
_Out_opt_ PVOID pvData,
_Inout_opt_ LPDWORD pcbData
);
Full reference here: http://msdn.microsoft.com/en-us/library/ms724875(v=VS.85).aspx
One thing that stands out are all the calls where you do this:
RegGetValue(hKey, NULL, "PATH", RRF_RT_ANY, NULL, (PVOID)&ValueBuffer, &BufferSize);
Notice the last parameter is &BufferSize. This is an [in, out] parameter. You set it to the size of your buffer before going in and it changes the value to the number of characters read into the buffer on the way out. This is what the docs say about RegGetValue (and other similar Reg) functions:
pcbData [in, out, optional]
A pointer to a variable that specifies the size of the buffer pointed to by the pvData parameter, in bytes. When the function returns, this variable contains the size of the data copied to pvData.
So when you call RegGetValue it started at 8192 (BUFFER) but after the first call it was overwritten by the the number of characters read.
Before each RegGetValue call where you pass a &BufferSize you should do this:
BufferSize = BUFFER
As well I notice you have:
#define BUFFER 8192
char ValueBuffer[255];
DWORD BufferSize = BUFFER;
Shouldn't you be setting ValueBuffer to be at least ValueBuffer[BUFFER] instead of ValueBuffer[255]?
As well your code as it is only supports an 8192 byte buffer. If the Value key from the registry is longer than that then it would return ERROR_MORE_DATA. I assume that your code is not anticipating anything beyond 8192. You can determine the size up front for a Value key by using RegQueryInfoKey and dynamically allocating enough space ahead of time.
ERROR_MORE_DATA means that one of the buffers you passed to RegGetValue is not large enough to store the data you asked RegGetValue to give you. You need to loop and reallocate the buffers supplied here when you get this exit code.
For example:
LONG result = ERROR_MORE_DATA;
DWORD dataLength = /* some reasonable default size */ 255;
unique_ptr<char[]> buffer;
while (result == ERROR_MORE_DATA)
{
buffer.reset(new char[dataLength]);
result = RegGetValueW(
hKey,
L"Subkey",
L"Value",
0,
nullptr,
buffer.get(),
&dataLength
);
}
if (result != ERROR_SUCCESS)
{
// Handle the error
}