How can I start explorer.exe via C++? - c++

I'm trying to programmatically start explorer.exe but I'm not having any luck.
This is my code:
cout << pName << "died, lets restart it." << endl;
STARTUPINFO startupInfo = {0};
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInformation;
if(CreateProcess(pName, NULL, NULL, NULL, false, NORMAL_PRIORITY_CLASS, NULL, NULL, &startupInfo, &processInformation) == 0){
cout << "Error starting " << pName << ": " << GetLastError() << endl;
}
and pName is explorer.exe
Can someone tell me what I'm doing wrong? I get the error code '2' which is ERROR_FILE_NOT_FOUND

The first parameter is the application name; the second is the command line. Try specifying "explorer.exe" as the second parameter.
See this MSDN article:
lpApplicationName [in, optional]
The name of the module to be executed.
This module can be a Windows-based
application. It can be some other type
of module (for example, MS-DOS or
OS/2) if the appropriate subsystem is
available on the local computer.
The string can specify the full path
and file name of the module to execute
or it can specify a partial name. In
the case of a partial name, the
function uses the current drive and
current directory to complete the
specification. The function will not
use the search path. This parameter
must include the file name extension;
no default extension is assumed.

You probably should give "ShellExecuteEx" a try. This function lets you specify a file or folder and a verb that describes what to do with it. If you use "explore" as the verb, it will open Windows Explorer with the given folder.

It's surprisingly hard to find relevant information on how to reliably restart windows explorer. On 64-bit Windows 7/8, the ShellExecute method does not work properly and leads to things such as file copying and icon overlays being completely broken.
The most reliable way seems to use stdlib.h system call:
system("start explorer");
If you are trying to shutdown and restart explorer, you might want to programmatically disable AutoRestartShell registry key, which prevents you from controlling when explorer is restarted.

Related

How can I run a .exe file from a windows service using c++

I'm new to the windows services and visual studio. I am trying to start a .exe file from a wind32 application. The code works fine and there is no error. I am using a CreateProcess() method and checked whether the method is running properly. There is no issues in it. The .exe file which i am calling simply creates text document. When i call that .exe file from console, it works fine, it creates the file. But when I call it from the wind32 app, it does not create any file. I am using Visual studio 2019. This is my code for calling the .exe file. `
STARTUPINFO info;
PROCESS_INFORMATION processInfo;
ZeroMemory(&info, sizeof(info));
info.cb = sizeof(info);
ZeroMemory(&processInfo, sizeof(processInfo));
LPCWSTR path = L"C:\\HP\\...(pathofexe).exe";
bool bSuccess = CreateProcess(path, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo);
if (bSuccess)
{
cout << "Success";
}
else
{
cout << "Error : " << GetLastError() << endl;
}`
Rgarding the working directory mismatch that was discussed in the comments, The file may not be there because it's simply somewhere else.
You call:
CreateProcess(path, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo);
Assuming you've read the documentation, you know the 8th parameter specifies the directory where the new process will run. Because you use NULL, this directory will be the same as that of the caller process, I assume "C:\\Users\\HP\\source\\repos\\ThisThingsName\\Debug\\".
That alone is not a problem, but given the context I believe the callee (SampleService.exe) calls the file function from its relative path, "\\Success.txt", rather than the full path, is that right?
In that case, when you open SampleService.exe manually, Success.txt would appear in "C:\\Users\\HP\\source\\repos\\SampleService\\Debug\\". However, when you open it using CreateProcess, it runs in "C:\\Users\\HP\\source\\repos\\ThisThingsName\\Debug\\" (because you did not specify otherwise), which is also where Success.txt would appear.
As I type this, I'm starting to doubt it, actually. Haven't you checked where it appears in Explorer?

Problems with some system default .lnk-files launching from under an impersonated user

I'm writing the 32bit service app where I want to be able to launch Start menu items for the logged users. I did manage to accomplish this task by impersonating user and launching selected .lnk-file using CreateProcessAsUser with command-line: %windir%\system32\cmd /c " start /b /i "" "<path-to-lnk-file>" ". And it works for almost every shortcut except bunch of the system shortcuts from Accessories folder (e.g. Sticky Notes.lnk, Snipping Tool.lnk). During the launch of the Snipping Tool I'm receiving the message box with this error from cmd:
Windows cannot find 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Snipping Tool.lnk'. Make sure you typed the name correctly, and then try again.
But the .lnk-file exists in this very directory!
Summary:
service is 32 bit
Windows 8 Pro x64
launching shortcuts by user impersonation and CreateProcessAsUser with command-line %windir%\system32\cmd /c " start /b /i "" "<path-to-lnk-file>" "
approach works for almost every shortcut in Start menu except some in Start/Accessories folder (not all of them, e.g. Paint.lnk opens fine)
example code:
int launchAppForCurrentLoggedUser()
{
HANDLE userToken = WTSApiHelper::currentLoggedUserToken();
if (userToken == INVALID_HANDLE_VALUE) {
return -1;
}
//Duplicating token with access TOKEN_DUPLICATE | TOKEN_ALL_ACCESS,
//impersonation level SecurityImpersonation and token type TokenPrimary.
//Also closing original userToken
HANDLE dup = WTSApiHelper::duplicateToken(userToken);
if (dup == INVALID_HANDLE_VALUE) {
return -1;
}
int res = -1;
uint8 *env = NULL;
BOOL succeeded = CreateEnvironmentBlock((LPVOID *)&env, dup, FALSE);
if (!succeeded) {
Log("failed to get environment variables for user (error 0x%x).", GetLastError());
}
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
STARTUPINFOW si;
memset(&si, 0, sizeof(STARTUPINFOW));
si.cb = sizeof(STARTUPINFOW);
si.lpDesktop = L"winsta0\\Default";
WCHAR params[] = L"/c \" start /b /i \"\" \"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Accessories\\Snipping Tool.lnk\" \" ";
WCHAR cmd[] = L"C:\\Windows\\system32\\cmd.exe";
DWORD flags = env ? CREATE_UNICODE_ENVIRONMENT : 0;
succeeded = CreateProcessAsUserW(dup, cmd, params, NULL, NULL, FALSE, flags | CREATE_NO_WINDOW, env, NULL, &si, &pi);
if (!succeeded) {
Log("cannot launch process for user with error 0x%x.", GetLastError());
} else {
nres = 0;
}
DestroyEnvironmentBlock(env);
CloseHandle(dup);
return nres;
}
What do I miss here?
It's not the LNK file that is missing, but its target.
Seems like a WOW64 issue -- for your 32-bit service, %WINDIR%\System32 actually redirects to SysWOW64 and these executable files do not exist there.
Well, actually your 32-bit service is finding the 32-bit cmd.exe which does exist in SysWOW64, and then 32-bit cmd.exe has the above problem, when looking up the path %windir%\system32\SnippingTool.exe found in the shortcut .
I can reproduce the problem using a 32-bit Command Prompt. 32-bit processes attempting to use these shortcuts simply fail.
Try spawning the native version of cmd.exe (64-bit on your system), using %WINDIR%\SysNative\cmd.exe
In addition, you have quoting problems. You're trying to nest quotes, but what actually happens is that the second quote matches the first quote and exits quoting, rather than nesting.
In the future, when things fail in a service it is helpful to run the same call from a normal console application. In this case you would have immediately discovered that the issue is completely unrelated to impersonation. Second step, if it worked from a console application running in-profile would be using "Run As" with the console application, to test impersonation logic, still without the additional complexity of the service environment.
CreateProcessAsUser does not load the specified user's profile into the HKEY_USERS registry key. Therefore, to access the information in the HKEY_CURRENT_USER registry key, you must load the user's profile information into HKEY_USERS with the LoadUserProfile function before calling CreateProcessAsUser. Be sure to call UnloadUserProfile after the new process exits.
According to the msdn page
MSDN suggests to use CreateProcessWithLogonW or CreateProcessWithTokenW, or to manually load the user's profile information.
And also:
CreateProcessAsUser allows you to access the specified directory and executable image in the security context of the caller or the target user. By default, CreateProcessAsUser accesses the directory and executable image in the security context of the caller. In this case, if the caller does not have access to the directory and executable image, the function fails. To access the directory and executable image using the security context of the target user, specify hToken in a call to the ImpersonateLoggedOnUser function before calling CreateProcessAsUser.

How to find the parent exe of a dll from inside the dll?

I need to do some stuff in a dll based on which process has loaded it. So being relatively new to windows programming I need help figuring out how to find the exe which loaded the current dll. So far I have been hard coding the exe file name, which is the dumbest thing to do :D
1) Some one suggested using GetModuleFileName() function. But this seems to crash my app.(I used 0 as the module handle). I am doing nothing fancy. I used the following syntax
GetModuleFileName(0,&fileName,MAX_PATH)
EDIT: I understood from here that I cannot get the .exe name with this call as it returns only the dll name :(
2)Is it a good idea to do this in the DllMain ?? I know that DllMain is not the place to do complicated stuff. I also understand loader lock related issues.All I need is to find the name of the parent process.
I appreciate your time !
ADD: I tried to use GetProcessImageFileName after getting the parent process ID. I get an access violation error. When I tried to debug I noticed that the openProcess call leaves my result argument(image file path-LPTSTR) as a bad pointer.
Error code 87-INVALID PARAMETER is returned by the GetProcessImageFileName call.
But the current process id is a valid id.
Here is the code
LPTSTR fileName={0};
HANDLE hP=OpenProcess(PROCESS_QUERY_INFORMATION ,FALSE, processes[i]) ;
GetProcessImageFileName(hP,fileName,(DWORD)MAX_PATH+1);
What Am I doing wrong??
Thanks
EDIT IMPORTANT:
I found out that I am trying to use openprocess on an idle process. (i.e) I forgot that my parent process could possibly be waiting idle for me since I sync it . So now I got the bad news that I cannot open an idle process using OpenProcess. How else can i get to look into the object of an Idle process?? (I know for sure its idle because I could not find it in the snapshot. I had to use enumerateprocess to locate its id; But i do use normal process enumeration from the snapshot to find the parent process id in the first place)
If you have declared your fileName variable as something like char fileName or char fileName[MAX_PATH], you may receive an error because your parameter is incorrect: you use the address of the variable (though, you don't specify whether it is a compile time error or runtime error, you say it crashes your app, so I go with Richard here, you've not allocated your variable).
I tried the following code, which works both from within a DLL (it gets the name of the executable, not the DLL module) or from within the executable itself.
(Note: code updated based on Remy's comments below, thanks)
WCHAR exePath[MAX_PATH + 1];
DWORD len = GetModuleFileNameW(NULL, exePath, MAX_PATH);
if (len > 0) {
wcout
<< L"Exe path"
<< (len == MAX_PATH) ? L" (truncated):" : L":"
<< exePath
<< endl;
} else {
wcout
<< L"Error getting exe path: "
<< GetLastError()
<< endl;
}
Note: if the buffer is not large enough, GetModuleFileName will truncate the result and return nSize.
More on handling filenames in Win32.
Refer the following link to know about the syntax and the detailed description about the GetModuleFileName()
Steps to do:
First get the full path of the executable file using the code:
TCHAR szEXEPath[2048];
char actualpath[2048];
GetModuleFileName ( NULL, szEXEPath, 2048 );
for(int j=0; szEXEPath[j]!=0; j++)
{
actualpath[j]=szEXEPath[j];
}
From the full path of the executable file, split the string to get only the executable name using the built in function str.find_last_of()
std::string str (actualpath);
std::size_t found = str.find_last_of("/\\");
std::cout<< str.substr(found+1) << '\n';
Now you can get only the executable file name.
I assume you are coding in C. You most likely have not allocated MAX_PATH + 1 characters for filename.

Getting File Associations using Windows API

I'm working on a console based file browser for Windows in C++ and am having difficulties getting together a context menu that lists actions associated with a file and calls commands on them. The biggest issue right now is getting the actions tied to the file types.
I know of the process to open and tweak the registry keys in HKEY_CLASSES_ROOT but I can't find a way to actually get the actions and their commands so I can build a context menu out of it.
The general structure of these associations in the registry is:
HKEY_CLASSES_ROOT\(extension)\(default) - filetype
HKEY_CLASSES_ROOT\filetype\(default) - description of the filetype
HKEY_CLASSES_ROOT\filetype\shell\action\(default) - description of the action
HKEY_CLASSES_ROOT\filetype\shell\action\command\(default) - command called on file
I'm wondering if there is a way (hopefully using the Windows API) that I can get all of the actions associated with a file type. At least then I can check those actions for their commands in the registry...
Also, this approach doesn't seem to work with some common file types (e.g. mp3) on my system as the default key is left blank and another key ("PercievedType") is set to audio... How can I get the actions for something like this?
Lastly, if there is a better way to do this in general I would love to hear it, I generally hate dealing with the registry. I would much rather have a simple windows call that would get me the actions and commands...
Try this (error handling omitted for brevity):
TCHAR szBuf[1000];
DWORD cbBufSize = sizeof(szBuf);
HRESULT hr = AssocQueryString(0, ASSOCSTR_FRIENDLYAPPNAME,
argv[1], NULL, szBuf, &cbBufSize);
if (FAILED(hr)) { /* handle error */ }
CStringA strFriendlyProgramName(szBuf, cbBufSize);
cbBufSize = sizeof(szBuf);
hr = AssocQueryString(0, ASSOCSTR_EXECUTABLE,
argv[1], NULL, szBuf, &cbBufSize);
if (FAILED(hr)) { /* handle error */ }
CStringA strExe(szBuf, cbBufSize);
std::cout << strFriendlyProgramName << " (" << strExe << ")" << std::endl;
Consider using IContextMenu. IContextMenu is how Windows Explorer accesses the context menu for files and items.
This article by Raymond Chen has sample code for how to access IContextMenu for a given file path and use it to fill an HMENU with the set of available commands. It's the first of a series of articles that give a decent overview along with sample code.

Trouble restarting exe

I need to restart the program that im working on after an update has been downloaded except im running into some issues.
If i use CreateProcess nothing happens, if i use ShellExecute i get an 0xC0150002 error and if i use ShellExecute with the command "runas" it works fine. I can start the command prompt fine using CreateProcess and ShellExecute just not the same exe again and dont want to use runas as this will elevate the exe.
Any Ideas?
Windows 7, visual studio 2008 c++
alt text http://lodle.net/shell_error.jpg
CreateProcess:
char exePath[255];
GetModuleFileName(NULL, exePath, 255);
size_t exePathLen = strlen(exePath);
for (size_t x=exePathLen; x>0; x--)
{
if (exePath[x] == '\\')
break;
else
exePath[x] = '\0';
}
char name[255];
GetModuleFileName(NULL, name, 255);
PROCESS_INFORMATION ProcInfo = {0};
STARTUPINFO StartupInfo = {0};
BOOL res = CreateProcess(name, "-wait", NULL, NULL, false, 0, NULL, exePath, &StartupInfo, &ProcInfo );
ShellExecute:
char exePath[255];
GetModuleFileName(NULL, exePath, 255);
size_t exePathLen = strlen(exePath);
for (size_t x=exePathLen; x>0; x--)
{
if (exePath[x] == '\\')
break;
else
exePath[x] = '\0';
}
char name[255];
GetModuleFileName(NULL, name, 255);
INT_PTR r = (INT_PTR)ShellExecute(NULL, "runas", name, "-wait", exePath, SW_SHOW);
CreateProcess() is an arcane beast. I remember unfondly my first frustrations with it. You should look at the Microsoft CreateProcess Example and the CreateProcess Page. (those links likely have a short lifetime, Googling CreateProcess should work just as well).
I can see 3 problems in your code.
StartupInfo must have "cb" set to the structure size:
STARTUPINFO StartupInfo = {0};
StartupInfo.cb = sizeof(StartupInfo);
The second argument requires both the command and the arguments to form the command line. Your program will see "-wait" as argv[0] and ignore it or pay it no mind.
char command[512];
sprintf(command, "%s -wait", name);
BOOL res = CreateProcess(name, command, // and as you had before
You don't look at GetLastError() if CreateProcess() fails (by returning a zero). It may have helped you but I suspect it would just say "invalid argument" or somesuch. Hey, there's only 10 of them to check, don't be lazy :-)
Another bug I committed is not closing the hProcess and/or hThread handles return in PROCESS_INFORMATION when I was done. I did do hProcess, but not hThread.
Looks like a manifest or registry question judging from the error code. If you can't get the actual error message string for more details, you might try:
moving every possible manifest file (Microsoft.VC80.CRT.manifest and the like) into your exe's directory, to ensure accessibility
cleanly and completely uninstall/wipe out old versions of DLL you may have installer newer versions of (I suggest: uninstall EVERY version, clean the registry with a sweep-clean tool such as Norton's, reinstall the new stuff from scratch)
What happens if you run the process using system()? It gives you less control, but you'll be running it from the same context you're running in. Also, Try monitoring the launch of your second process using ProcMon, it might give you the hint you need about the source of the failure.
Ok worked it all out in the end.
The first time my exe ran it used the default paths and as such loaded vld (a leak detector dll) from the default path. However in the exe i modified the dll path to be the bin folder ([app]\bin) when i restarted the exe using CreateProcess it picked up on a different vld dll (this was my mistake) that had incorrect side by side linkage and it was only after looking at event viewer that i worked it out.
Thanks for all your help.