2 * PROJECT: ReactOS Local Spooler API Tests
3 * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
4 * PURPOSE: Functions needed to run our code as a service. This is needed to run in SYSTEM security context.
5 * COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
10 #define WIN32_NO_STATUS
20 #include "localspl_apitest.h"
34 PVOID pLoadLibraryAddress
;
35 PVOID pLoadLibraryArgument
;
37 WCHAR wszFilePath
[MAX_PATH
];
39 // Get the full path to our EXE file.
40 if (!GetModuleFileNameW(NULL
, wszFilePath
, _countof(wszFilePath
)))
42 DPRINT("GetModuleFileNameW failed with error %lu!\n", GetLastError());
46 // Replace the extension.
47 p
= wcsrchr(wszFilePath
, L
'.');
50 DPRINT("File path has no file extension: %S\n", wszFilePath
);
55 cbDLLPath
= (wcslen(wszFilePath
) + 1) * sizeof(WCHAR
);
57 // Create a snapshot of the currently running processes.
58 hSnapshot
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
59 if (hSnapshot
== INVALID_HANDLE_VALUE
)
61 DPRINT("CreateToolhelp32Snapshot failed with error %lu!\n", GetLastError());
65 // Enumerate through all running processes.
66 pe
.dwSize
= sizeof(pe
);
67 if (!Process32FirstW(hSnapshot
, &pe
))
69 DPRINT("Process32FirstW failed with error %lu!\n", GetLastError());
75 // Check if this is the spooler server process.
76 if (wcsicmp(pe
.szExeFile
, L
"spoolsv.exe") != 0)
79 // Open a handle to the process.
80 hProcess
= OpenProcess(PROCESS_ALL_ACCESS
, FALSE
, pe
.th32ProcessID
);
83 DPRINT("OpenProcess failed with error %lu!\n", GetLastError());
87 // Get the address of LoadLibraryW.
88 pLoadLibraryAddress
= (PVOID
)GetProcAddress(GetModuleHandleW(L
"kernel32.dll"), "LoadLibraryW");
89 if (!pLoadLibraryAddress
)
91 DPRINT("GetProcAddress failed with error %lu!\n", GetLastError());
95 // Allocate memory for the DLL path in the spooler process.
96 pLoadLibraryArgument
= VirtualAllocEx(hProcess
, NULL
, cbDLLPath
, MEM_COMMIT
| MEM_RESERVE
, PAGE_READWRITE
);
97 if (!pLoadLibraryArgument
)
99 DPRINT("VirtualAllocEx failed with error %lu!\n", GetLastError());
103 // Write the DLL path to the process memory.
104 if (!WriteProcessMemory(hProcess
, pLoadLibraryArgument
, wszFilePath
, cbDLLPath
, NULL
))
106 DPRINT("WriteProcessMemory failed with error %lu!\n", GetLastError());
110 // Create a new thread in the spooler process that calls LoadLibraryW as the start routine with our DLL as the argument.
111 // This effectively injects our DLL into the spooler process and we can inspect localspl.dll there just like the spooler.
112 hThread
= CreateRemoteThread(hProcess
, NULL
, 0, (LPTHREAD_START_ROUTINE
)pLoadLibraryAddress
, pLoadLibraryArgument
, 0, NULL
);
115 DPRINT("CreateRemoteThread failed with error %lu!\n", GetLastError());
119 CloseHandle(hThread
);
122 while (Process32NextW(hSnapshot
, &pe
));
126 _ServiceControlHandlerEx(DWORD dwControl
, DWORD dwEventType
, LPVOID lpEventData
, LPVOID lpContext
)
132 _ServiceMain(DWORD dwArgc
, LPWSTR
* lpszArgv
)
134 SERVICE_STATUS_HANDLE hServiceStatus
;
135 SERVICE_STATUS ServiceStatus
;
137 UNREFERENCED_PARAMETER(dwArgc
);
138 UNREFERENCED_PARAMETER(lpszArgv
);
140 // Register our service for control.
141 hServiceStatus
= RegisterServiceCtrlHandlerExW(SERVICE_NAME
, _ServiceControlHandlerEx
, NULL
);
143 // Report SERVICE_RUNNING status.
144 ServiceStatus
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
145 ServiceStatus
.dwServiceSpecificExitCode
= 0;
146 ServiceStatus
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
147 ServiceStatus
.dwWaitHint
= 4000;
148 ServiceStatus
.dwWin32ExitCode
= NO_ERROR
;
149 ServiceStatus
.dwCurrentState
= SERVICE_RUNNING
;
150 SetServiceStatus(hServiceStatus
, &ServiceStatus
);
152 // Do our funky crazy stuff.
156 ServiceStatus
.dwCurrentState
= SERVICE_STOPPED
;
157 SetServiceStatus(hServiceStatus
, &ServiceStatus
);
165 SERVICE_TABLE_ENTRYW ServiceTable
[] =
167 { SERVICE_NAME
, _ServiceMain
},
171 // This is no real test, but an easy way to integrate the service handler routines into the API-Test executable.
172 // Therefore, bail out if someone tries to run "service" as a usual test.
173 argc
= winetest_get_mainargs(&argv
);
177 // If we have exactly 3 arguments, we're run as a service, so initialize the corresponding service handler functions.
178 StartServiceCtrlDispatcherW(ServiceTable
);
180 // Prevent the testing framework from outputting a "0 tests executed" line here.