Genearting screenshot (BMP) with Windows Service and CreateProcessWithLogonW - c++

I'm trying to create a screenshot application using windows service, that can capture complete screen at regular time intervals. To get started with, I took basic window service example (http://code.msdn.microsoft.com/windowsdesktop/CppWindowsService-cacf4948) and included screenshot utility class in the project and called the capture function in the onStart method of sample windows service. However, I got blank screen shots. Later I realized that windows service runs in different session and hence the screenshots are blank.
S, I decoupled the screenshot utility class and made it as a seperate project and generated a .exe out of it (which is capable of writing logs to a file where ever needed). This time, I used createProcessWithLogonW function to call the executable, so that it will run the exe in specified user domain than the default service session. The following is the code snippet I have been using.
void CSampleService::StartProcess()
{
DWORD dwSize; HANDLE hToken=NULL;
LPVOID lpvEnv; PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
WCHAR szUserProfile[256] = L"";
si.cb = sizeof(STARTUPINFO);
if (!CreateEnvironmentBlock(&lpvEnv, hToken, TRUE))
{
logger::Wlog(logger::fileName,"CreateEnvironmentBlock Error");
}
dwSize = sizeof(szUserProfile)/sizeof(WCHAR);
GetUserProfileDirectory(hToken, szUserProfile, &dwSize);
CreateProcessWithLogonW(L"admin", L"MyDomain",L"mypassword",
LOGON_WITH_PROFILE, NULL, L"C:\\Temp\\application.exe",
CREATE_UNICODE_ENVIRONMENT, lpvEnv, NULL, &si, &pi);
DestroyEnvironmentBlock(lpvEnv);
CloseHandle(hToken);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
I have no errors from the logs i got. when the services get initiated, it gives a warning of interactive service trying to show a message. When I click that, I received a black screenshot again. I know that there are many things involved here, I just gave a high level view of what I'm doing. Also, i'm pretty new to windows programming. Any kind of help is appreciated.

yes, i got that working. we have to acquire the active session-ID (perhaps from opened desktop) and run the process as interactive service in that session context. This should probably solve the capturing screenshots issue. If someone is looking for GUI and service interactions perhaps IPC will be the most convenient way.

Related

How to check Win32 CreateProcess() failed reason using ProcMon. exclude GetLastError()

I am having an issue with checking CreateProcess() failure reason, there was a code in production which doesn't log GetLastError() when CreateProcess() failed so i am running ProcMon to check the reason but unable to find the reason (Will procMon log the failure reason something like "C:\dummy.exe path not found or permission denied" ?).
Is there a way (tools ?) to check why CreateProcess() is failing without considering GetLastError() ?
I can't debug customer environment (no access to me) but I can change the code & provide new build & it takes long time due to process. i am currently looking for quick options available. Below is the sample code not exact production code.
int main()
{
STARTUPINFO info = { sizeof(info) };
PROCESS_INFORMATION processInfo;
TCHAR dymmypath[_MAX_PATH] = _T("C:\\dummy.exe");
static TCHAR TempPathString[_MAX_PATH];
STARTUPINFO si = { sizeof(si) }; //default set up
PROCESS_INFORMATION pi; //data structure for CreateProcess
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWMINIMIZED;
if (!CreateProcess(dymmypath, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, TempPathString, &si, &pi))
{
printf("Failed");
}
else {
printf("Success");
}
return 0;
}
i am running ProcMon to check the reason but unable to find the reason (Will procMon log the failure reason something like "C:\dummy.exe path not found or permission denied" ?).
Only if the request reaches the filesystem, ie to look for the EXE file, which in your case it sounds like it is not doing that, likely because CreateProcess() is failing to validate your input parameters before it reaches into the filesystem.
Is there a way (tools ?) to check why CreateProcess() is failing without considering GetLastError() ?
As others said, you could try attaching a debugger to your running app, and put a breakpoint in the CreateProcess function itself.
Another option is to use a tool like API Monitor, which will show you the actual API calls your program makes, what their parameter values are, reported error codes, etc.
I can't debug customer environment (no access to me) but I can change the code & provide new build
Then that is what you should do. Fix your code to do proper logging of error codes, don't ignore them anymore.
it takes long time due to process.
Well, that is your own fault for not optimizing your build process better, or breaking up your app into more manageable pieces, etc.
Just at first glance, I see TempPathString is initialized to "", which is not a valid path. So while you're fixing that issue, that's your chance to add proper error handling.
The tool you're looking for is a debugger. You should attach the debugger of your choice, set a breakpoint on the return of CreateProcess, and check the error there.
Besides debugging and error handling (logging etc), you'll have to just get creative. Compare a working environment against production for example.

Issues trying to display a configuration window for my screensaver (from a GUI app running with high mandatory integrity level)

I have an old project that comes with its own screensaver. The GUI application for the project has an option to "Configure screensaver" that should bring up my screensaver configuration window (that you would normally get from a Control Panel -> Display -> Personalization -> Screensaver -> Settings.)
To display the configuration window, one needs to call the screensaver process with the /c parameter as described here. The screensaver itself is placed into the system directory, i.e. "C:\Windows\System32".
So to emulate this from my (32-bit) GUI application I do the following:
//Error checks are omitted for brevity
BOOL bResult = FALSE;
TCHAR buffSysFldr[MAX_PATH];
buffSysFldr[0] = 0;
::GetSystemDirectory(buffSysFldr, SIZEOF(buffSysFldr));
//Make the path, which basically becomes:
// "C:\Windows\System32\mysvr.scr" /c
TCHAR buff[MAX_PATH];
buff[0] = 0;
::StringCbPrintf(buff, sizeof(buff), L"\"%s\\mysvr.scr\" /c", buffSysFldr);
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
ZeroMemory(&si,sizeof(si));
ZeroMemory(&pi,sizeof(pi));
si.cb = sizeof(si);
PVOID OldValue;
Wow64DisableWow64FsRedirection(&OldValue);
//And run it
if(CreateProcess(NULL, buff, NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
NULL, NULL, &si, &pi))
{
//Success
bResult = TRUE;
}
Wow64RevertWow64FsRedirection(OldValue);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
When I deploy it to the Windows 7 machine, the code above returns bResult = TRUE but my screensaver's configuration window is never shown.
I tried it on both 32-bit and 64-bit Windows 7 thinking that Wow64 redirection has something to do with it, but it still didn't change the outcome.
Any idea why this doesn't work?
PS. When I copy and paste that command into a command prompt it runs just fine and displays my configuration window w/o a problem:
EDIT: After further review, it turns out that the issue seems to be connected to the fact that my GUI applications runs with High Mandatory Integrity Level, or S-1-16-12288, which seems to be the reason why the screensaver's configuration window is not shown. If I run my GUI app with its regular Medium integrity level, or S-1-16-8192, the configuration window shows OK.
Otherwise, I can see that my screensaver's RegisterDialogClasses method is called OK, but then when I return TRUE from it, ScreenSaverConfigureDialog is never called.
Since integrity level was introduced to prevent processes with lower levels to send messages to processes with higher levels, evidently the screensaver configuration mechanism tries to send some messages to my GUI app (for whatever undocumented reason) and fails and then silently fails as well ...
So this is the latest. I'll appreciate any input on this from whoever knows how to solve this (apart from lowering the integrity level of my GUI app, that I obviously don't want to do.)
God damn Microsoft documentation (sorry, I just wasted the whole day today trying to fix it.)
For whoever else runs into this -- it turns out one needs to call it as such:
"C:\Windows\System32\mysvr.scr" /c:N
where N is a window handle of the parent window expressed as an integer. Found it out by checking command line of the configuration window displayed via Control Panel (did it using Process Explorer.)
In my case, since my GUI process runs with higher integrity level, doing the following will suffice:
HWND hWndToUse = ::GetDesktopWindow();
::StringCbPrintf(buff, sizeof(buff), L"\"%s\\mysvr.scr\" /c:%d", buffSysFldr, (int)hWndToUse);
And that's it!

How to control Windows services using Windows API easily?

Issue
I would like to control any Windows service using Windows API, but I cannot :(
= What's wrong with my "wanting solution"?
Current solution
The ugliest but functional way I use is following (for Stop command):
void StopService()
{
ShellExecute(Application->Handle,
"open",
"stop.bat",
NULL,
ExtractFilePath(Application->ExeName).c_str(),
SW_SHOW);
}
Where stop.bat is a Windows batch file with following commands:
::the command
sc stop "service name"
::a pause to see if something goes wrong
pause
It is very annoying to have to close launched cmd windows...
Wanting solution
The function bellow appears to be ok. If I try to perform a command to stop an unexisting service, it will show a error message. BUT when I try to run it for an existing service, it runs, but nothing happens with the service (if I ordain to stop it, it does nothing)...
void ControlService(AnsiString ServiceName, DWORD Order)
{
SC_HANDLE Scm, Svc;
SERVICE_STATUS Status;
Scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(Scm != 0)
{
Svc = OpenService(Scm, PChar(ServiceName.c_str()), SERVICE_ALL_ACCESS);
if(Svc != 0)
{
ControlService(Svc, Order, &Status);
// handle Status....
CloseServiceHandle(Svc);
}
else
{
ShowLastError();
}
CloseServiceHandle(Scm);
}
else
{
ShowLastError();
}
}
void ShowLastError()
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
// Display the string.
MessageBox(NULL, (char*)lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION);
// Free the buffer.
LocalFree(lpMsgBuf);
}
What's wrong?????
Two possible issues you might have run into:
You are checking state of service too soon. The ServiceControl returns immediately, without waiting for the service to complete the request. Your service could transition to SERVICE_STOP_PENDING, and in this case you will have to give it some time and check again.
There are dependent services in running state. You will have to enumerate dependent services, shut them down first, then stop your service.
On #2, there is good explanation with a sample code on MSDN. Here is a quote from the same page:
However, if the SCM determines that other running services are dependent on the specified service, it will not forward the stop request. Instead, it returns ERROR_DEPENDENT_SERVICES_RUNNING. Therefore, to programmatically stop such a service, you must first enumerate and stop its dependent services.

ERROR_INVALID_HANDLE on TerminateProcess (VS C++)

Disclaimer: This is part of the requirement of the program, so it's not meant for anything bad. Feel free to point out any misuse if you spot one. I'm a beginner in C++.
Basically, I'm trying to restart Outlook.exe on Windows using C++.
And this is the code I used to restart Outlook.
#include <TlHelp32.h>
void RestartOutlook() {
PROCESSENTRY32 Pc = { sizeof(PROCESSENTRY32) };
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
MODULEENTRY32 Mo = {sizeof (MODULEENTRY32) };
if(Process32First(hSnapshot, &Pc)){
do{
if(!_stricmp(Pc.szExeFile, "outlook.exe")) {
DWORD pid = Pc.th32ProcessID;
HANDLE hModuleSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
//kill outlook
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
DWORD fdwExit = 0;
GetExitCodeProcess(process, &fdwExit);
TerminateProcess(process, fdwExit);
char * path;
if (Module32First(hModuleSnapshot, &Mo)) {
path = Mo.szExePath;
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof (si);
CreateProcess(path, NULL, NULL, NULL, false, NORMAL_PRIORITY_CLASS,
NULL, NULL, &si, &pi);
}
}
}while(Process32Next(hSnapshot, &Pc));
}
}
The funny part is, this piece of code works perfectly fine on Windows 7. While on Windows XP (SP3), I get duplicated Outlook. The GetLastError gives me 6: ERROR_INVALID_HANDLE. I am really clueless after hours of research.
Any idea?
Anyway, C++ is not my field. I do webs :)
And the code above is a mixture of the following sources:
1: http://www.istorya.net/forums/programming/107435-how-can-i-kill-a-process-using-c.html
2: http://code.activestate.com/recipes/576362-list-system-process-and-process-information-on-win/
Environment: Windows 7, Windows XP, VS2010, Outlook 2003, Outlook 2007, Outlook 2010
I found the culprit.
The reason lies in this line:
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
According to http://msdn.microsoft.com/en-us/library/ms684880(v=vs.85).aspx, PROCESS_ALL_ACCESS is too large for Windows XP/NT system, or more details:
The size of the PROCESS_ALL_ACCESS flag increased on Windows Server 2008 and Windows Vista. If an application compiled for Windows Server 2008 and Windows Vista is run on Windows Server 2003 or Windows XP/2000, the PROCESS_ALL_ACCESS flag is too large and the function specifying this flag fails with ERROR_ACCESS_DENIED. To avoid this problem, specify the minimum set of access rights required for the operation.
Definitely I'm compiling this program on 7, while running on XP definitely causing the problem.
So the solution is, change the PROCESS_ALL_ACCESS to PROCESS_TERMINATE, which
HANDLE process = OpenProcess(PROCESS_TERMINATE, TRUE, pid);
Done!
Thanks #DReJ for quick replies :)
I get that you want Outlook to restart but calling TerminateProcess on Outlook seems like a bad idea in the first place. What if it's in the middle of writing a data file?
A better way would be to find all top-level windows that belong to Outlook, send them a WM_CLOSE and then wait for the process to exit. (You may also have to cope with the user having draft messages open which result in "are you sure" prompts, although if you are doing this in the first place then I assume you know the user isn't in the middle of something?)
An even better way would be to use Outlook's automation interface and tell it to shutdown explicitly.
Your problem maybe related to this piece of code
DWORD fdwExit = 0;
GetExitCodeProcess(process, &fdwExit);
TerminateProcess(process, fdwExit);
First, with GetExitCodeProcess you get status STILL_ACTIVE and after that you terminate process with this status which is not proper I think. Remove GetExitCodeProcess from you code and try TerminateProcess(process, 0); instead.

Using a handle to collect output from CreateProcess()

I am using CreateProcess() to run an external console application in Windows from my GUI application. I would like to somehow gather the output to know whether there were errors. Now I know I have to do something with hStdOutput, but I fail to understand what. I am new to c++ and an inexperienced programmer and I actually don't know what to do with a handle or how to light a pipe.
How do I get the output to some kind of variable (or file)?
This is what I have a the moment:
void email::run(string path,string cmd){
WCHAR * ppath=new(nothrow) WCHAR[path.length()*2];
memset(ppath,' ',path.length()*2);
WCHAR * pcmd= new(nothrow) WCHAR[cmd.length()*2];
memset(pcmd,' ',cmd.length()*2);
string tempstr;
ToWCHAR(path,ppath); //creates WCHAR from my std::string
ToWCHAR(cmd,pcmd);
STARTUPINFO info={sizeof(info)};
info.dwFlags = STARTF_USESHOWWINDOW; //hide process
PROCESS_INFORMATION processInfo;
if (CreateProcess(ppath,pcmd, NULL, NULL, FALSE, 0, NULL, NULL, &info, &processInfo))
{
::WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
delete[](ppath);
delete[](pcmd);
}
This code probably makes any decent programmer scream, but (I shouldn't even say it:) It works ;-)
The Question: How do I use hStdOutput to read the output to a file (for instance)?
Microsoft has an example in its knowledge base that demonstrates how to capture the output of a child console process. The basic principle is that the parent process creates pipes (one per standard handle to redirect) and passes the handles to CreateProcess.
The child process does not need to be modified for this to work, which is important if you do not have control over the child's source.
More information: How to spawn console processes with redirected standard handles