I have a function, that reads a registry key, get a path for a program there, and call a CreateProcessA with this path as second parameter. Debugging the application, it fails saying that the file was not found.
a) yes, the file exists
b) yes, i have access to execute the file
Question: The function that actually reads the reg key and give the path to CreateProcessA, doesn't escape the path: it means, CreateProcessA receives a string like "C:\Program Files\prog.exe" and not like "C:\\Program Files\\prog.exe". Is that the problem? Does exist any Windows Function to escape all backslashes automatically?
Common errors include not specifying the path to the executable as the first argument to CreateProcess, and not quoting the path to the executable in the second argument
CreateProcess( <exe path goes here> , <quoted exe path plus parameters goes here>, ... );
Like so:
std::wstring executable_string(_T("c:\\program files\\myprogram\\executable.exe"));
std::wstring parameters(_T("-param1 -param2"));
wchar_t path[MAX_PATH+3];
PathCanonicalize(path, executable_string.c_str());
path[sizeof(path)/sizeof(path[0]) - 1] = _T('\0');
// Make sure that the exe is specified without quotes.
PathUnquoteSpaces(path);
const std::wstring exe = path;
// Make sure that the cmdLine specifies the path to the executable using
// quotes if necessary.
PathQuoteSpaces(path);
std::wstring cmdLine = path + std::wstring(_T(" ")) + parameters;
BOOL res = CreateProcess(
exe.c_str(),
const_cast<wchar_t *>(cmdLine.c_str()),
...);
I just copied and adapted some, so there might be some errors in the above, but the idea is there. Make sure to use path without quotes in the first parameter and path with quotes in the second and you should be fine.
Related
When I run a commandline program (which internally deletes a log file) from CMD prompt, it's working as expected.
But the same command when run in a PowerShell prompt is not deleting the log file. The command is run successfully except for the log file deletion. There is no error or exception thrown from the PowerShell prompt.
How does PowerShell differ from a CMD prompt in the Windows environment with respect to file handling, in this case it's deleting a file?
Note: Both the CMD prompt and PowerShell are run as Administrator.
The source code of the program looks like this:
WIN32_FIND_DATA fd;
LPCWSTR search_path_wstr = ws.c_str();
HANDLE hFind = ::FindFirstFile(search_path_wstr, &fd);
wstring wsFilename(fd.cFileName);
string cFileName(wsFilename.begin(), wsFilename.end());
string absoluteFilename = strPath + "\\" + cFileName;
const char *filename = absoluteFilename.c_str();
remove(filename);
remove() is the function which deletes the file.
Update: I have tried changing remove() to DeleteFile(), the behavior is still same.
Update 2: I have found the root cause. PowerShell is returning an absolute path whereas the CMD prompt is returning a relative path. This is not part of the above code snippet.
Now I need to find whether the path is relative or not. There is a Windows function, PathIsRelative(), but it takes LPCWSTR as input and again some conversion is required.
My psychic powers tell me that the file name has non-ASCII characters in it and the error in the failing case is "file not found."
In the code, you copy wide characters into regular chars. For anything outside of ASCII, this won't do what you want.
Your code sample doesn't show how you get the source string or strPath.
It's possible that, when you enter the search string in the CMD case, it's got some non-ASCII characters that are representable in the current code page, and those values are copied to wide characters and then back without harm, and the remove works.
When you enter it in PowerShell, you probably get UTF-16 encoded text. When you copy those values back to regular chars, you're not getting the same string, so the remove probably fails with "file not found."
Don't do this:
string cFileName(wsFilename.begin(), wsFilename.end());
Work with wide strings consistently, without any conversions. If you must convert between wide and narrow strings, you must know the encoding and actually transcode the data, not just copy it.
I'm using CreateProcess this way:
resultCreate = CreateProcess(Command, CommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
//"Command" contains the executable file to execute
//"CommandLine" contains the parameters to pass to that executable
The parameters are the following:
Param1: "C:\Users\myuser\Desktop\file.dll"
Param2: "file" (module name)
Param3: " " (blank)
So the full CommandLine string would be:
"C:\Users\myuser\Desktop\file.dll" file " "
CreateProcess runs the executable successfully and applies the first two parameters, but when reaches the third one, it throws error
The specified process could not be found.
Function " " could not be called, due to " " doesn't exist in the DLL "(null)"
How can I pass the desired parameters correctly?
When both lpApplicationName and lpCommandLine are used, you need to include the executable path as the first parameter of your lpCommandLine value, even though you are specifying the executable path in the lpApplication value. The lpCommandLine is passed as-is to the spawned process, and most RTLs (especially C-based RTLs, but others as well) expect the first command-line parameter to be the executable path since that is how command-line consoles operate. This is even mentioned in the CreateProcess() documentation:
If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to by lpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.
This is also covered in MSDN Support:
INFO: Understanding CreateProcess and Command-line Arguments
Behavior of CreateProcess() When Creating a 32-bit Process
Case 1:
If the ApplicationName parameter is passed and the CommandLine parameter is NULL, then the ApplicationName parameter is also used as the CommandLine. This does not mean that you can pass additional command-line parameters in ApplicationName string. For example, the following call will fail with a "File Not Found" error:
CreateProcess( "c:\\MyApp.exe Param1 Param2", NULL, ... )
Case 2:
On the other hand, if the CommandLine parameter is non-NULL and the ApplicationName parameter is NULL, then the API attempts to extract the application name from the CommandLine parameter.
Case 3:
The flexibility of the CreateProcess() function (and a possible point of confusion) arises when you pass a valid string pointer to both the ApplicationName and CommandLine parameters. This allows you to specify the application to be executed as well as the complete command line that is passed to the application. One might assume that the command line passed to the created application is a composite of the ApplicationName and CommandLine parameters, but this is not the case. As a result, a process created by CreateProcess can receive a value other than its .exe name as its "argv[0]" parameter. The following is an example of a call to CreateProcess that produces this "abnormal" behavior:
CreateProcess( "c:\\MyApp.exe", "Param1 Param2 Param3", ...)
MyApp's arguments will be as follow:
argv[0] == "Param1"
argv[1] == "Param2"
argv[2] == "Param3"
NOTE: ANSI specifications require that argv[0] should be equal to the application name, but CreateProcess gives the calling application the flexibility to override this rule for 32-bit processes.
Case #3 applies to your situation.
Use something like this instead:
std::string Command = "<exe path>";
std::string CommandLine = "\"" + Command + "\" <parameters>";
// std::string::c_str() returns a const pointer. The first parameter
// of CreateProcessA() is const, but the second parameter must be a
// non-const pointer to writable memory, because CreateProcessW() can
// modify the data...
//
resultCreate = CreateProcessA(Command.c_str(), &CommandLine[0], ...);
std::wstring Command = L"<exe path>";
std::wstring CommandLine = L"\"" + Command + L"\" <parameters>";
// std::wstring::c_str() returns a const pointer. The first parameter
// of CreateProcessW() is const, but the second parameter must be a
// non-const pointer to writable memory, because CreateProcessW() can
// modify the data...
//
resultCreate = CreateProcessW(Command.c_str(), &CommandLine[0], ...);
Alternatively, omit lpApplicationName and specify the complete command line to lpCommandLine only:
std::string CommandLine = "\"<exe path>\" <parameters>";
resultCreate = CreateProcessA(NULL, &CommandLine[0], ...);
std::wstring CommandLine = L"\"<exe path>\" <parameters>";
resultCreate = CreateProcessW(NULL, &CommandLine[0], ...);
I successfully write to a file in the folder which run example:
// I run "test" executable file in "TestWrite File" folder
const char *path="/home/kingfisher/Desktop/TestWrite File/xml/kingfisher.txt";
std::ofstream file(path); //open in constructor
std::string data("data to write to file");
file << data;
However, If I try to write with dynamic path: *path = "/xml/kingfisher.txt", it goes wrong (in Windows, it will be fine)!! How I can write with dynamic path like above (not a specific path)? Thanks!
If by dynamic you mean relative, you need to get rid of the leading /, since that makes it an absolute path:
path = "xml/kingfisher.txt";
Just be aware that this file is relative to your current working directory so you will probably need to ensure that it is set to /home/kingfisher/Desktop/TestWrite File for this to work.
If, by dynamic, you mean changable, you can change it whenever you want:
const char *path = "/tmp/dummy";
:
path = "/home/.profile"; // Note path, NOT *path
The const simply means you're not permitted to change the data behind the pointer. You're able to change the pointer itself at will.
Not sure what you mean by "dynamic path"; a dynamic path is one that
will be read dynamically (and so will probably be in an std::string).
On the other hand, you seem to be confusing absolute path and relative
path. If the filename begins with a '/' (under Unix), or with a '/'
or a '\\', possibly preceded by "d:" under
Windows, it is absolute; the search for the file will start at the root
of the file system (on the specified drive in the case of Windows). In
all other cases, it is relative; the search for the file will start at
the current working directory. In your example, both
"/home/kingfisher/Desktop/TestWrite File/xml/kingfiger.txt" and
"/xml/kingfisher.txt" are absolute. If the current working directory
is "/home/kingfisher/Desktop/TestWrite File", then
"xml/kingfisher.txt" should find the file specified by the first
absolute pathname.
*path = "/xml/kingfisher.txt"
This is incorrect since it attempts to dereferences your const char* and modify the contents. This is undefined behaviour since the data is const.
Just declare your path to be a std::string to begin with:
std::string path = "/home/kingfisher/Desktop/TestWrite File/xml/kingfisher.txt";
Then later you can assign any other value you like to the std string and it's operator= will dynamically change it's internals for you:
path = "my/new/path";
You can use this with ofstream just as before and if you need to pass it to a function which expects a const char * just pass path.c_str().
I am attempting to open a .chm file(A windows help file) at a specific page/topic by using a system call in C++.
I can successfully open the .chm file to the start page through the following code, but how can I open a .chm file to a specific page/topic inside the help file?
system("start c:/help/myhelp.chm");
PS: I know system is evil/discouraged but the system part is not really relevant its the command line arguments I pass with the .chm file(that will specify what page I want to open) that I am trying to determine.
Ok the arguments are like so:
system(" /Q /E:ON /C HH.EXE ms-its:myChm.chm::myPageName.htm");
There is an API in the Windows SDK called HtmlHelp in the HtmlHelp.h file. You can call like so:
HtmlHelp(GetDesktopWindow(), L"C:\\helpfile\\::/helptopic.html", HH_DISPLAY_TOPIC, NULL);
The Microsoft Docs - HtmlHelpA function provides more information about the function. HtmlHelp() will normally resolve to HtmlHelpA() or HtmlHelpW() depending on whether Unicode compiler option is set or not.
See as well Microsoft Docs - HTML Help API Overview.
Another option - use ShellExecute. The Microsoft help is not easy to use. This approach is much easier and in line with your question. Here is a quick routine to open a help file and pass an ID number. I have just set up some simple char’s so you can see what is going on:
void DisplayHelpTopic(int Topic)
{
// The .chm file usually has the same name as the application - if you don’t want to hardcode it...
char *CmndLine = GetCommandLine(); // Gets the command the program started with.
char Dir[255];
GetCurrentDirectory (255, Dir);
char str1[75] = "\0"; // Work string
strncat(str1, CmndLine, (strstr(CmndLine, ".exe") - CmndLine)); // Pull out the first parameter in the command line (should be the executable name) w/out the .exe
char AppName[50] = "\0";
strcpy(AppName, strrchr(str1, '\\')); // Get just the name of the executable, keeping the '\' in front for later when it is appended to the directory
char parms[300];
// Build the parameter string which includes the topic number and the fully qualified .chm application name
sprintf(parms,_T("-mapid %d ms-its:%s%s.chm"), Topic, Dir, AppName);
// Shell out, using My Window handle, specifying the Microsoft help utility, hh.exe, as the 'noun' and passing the parameter string we build above
// NOTE: The full command string will look like this:
// hh.exe -mapid 0 ms-its:C:\\Programs\\Application\\HelpFile.chm
HINSTANCE retval = ShellExecute(MyHndl, _T("open"), _T("hh.exe"), parms, NULL, SW_SHOW);
}
The topics are numbered within your .chm file. I set up a #define for each topic so if I had to change the .chm file I could just change the include file to match and not have to worry about searching through the code for hardcoded values.
Suppose that I have a dll called it MyDll.dll
It is in the d:\MyWorks\MyDll.dll [it is directshow dll]
I want to get path of its location from inside the MyDll code.
I used boost for this: FileSystem
string path = "";
boost::filesystem::path full_path( boost::filesystem::current_path() );
path = full_path.string();
But this give me its execution path, which is C:\Windows\system32, and not its location path which is d:\MyWorks\MyDll.dll.
How can I get a dll's location inside the same dll?
Update: By Get Module:
TCHAR path[2048];
GetModuleFileName( NULL, path, 2048 );
ostringstream file;
file << path ;
string const pathString =file.str();
cout << "Path: " << pathString << endl;
Gives me just hex-like string : 0049EA95....
In your DllMain you receive an HINSTANCE parameter; that is actually the HMODULE of your dll, that you can use with GetModuleFileName to retrieve the fully-qualified path of your dll. To get just the directory that contains it you just have to remove the file name (you can do that with boost::filesystem, with shell path functions as well as just with a strrchr).
Your problem is trying to see a Unicode string on an Ansi console output window. If you really want to see the result, you need to cast your strings to Ansi (with some loss of course) or you can directly use;
char path[2048];
GetModuleFileNameA(NULL, path, 2048);
cout << path;
If you want to use Unicode, use TCHAR and GetModuleFileNameW (or GetModuleFileName since your application is in unicode mode) but don't try to output to console window without casting to Ansi.
You can use GetModuleFileName in order to get the full path of a module.
The first argument is a handle to the required module. If this parameter is NULL, GetModuleFileName retrieves the path of the executable file of the current process.
If you want the path to other module, you may use GetModuleHandle to get a handle. For example:
TCHAR path[_MAX_PATH+1];
GetModuleFileName(GetModuleHandle(_T("MyDll.dll")), path, sizeof(path)/sizeof(path[0]));
TCHAR s[MAX_PATH+1];
GetModuleFileName(hInstance, s, _countof(s));
where hInstance is a parameter of DllMain. Despite the name, it returns the full path.