2 * PROJECT: ReactOS Local Spooler API Tests
3 * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
5 * COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
9 * The original localspl.dll from Windows Server 2003 is not easily testable.
10 * It relies on a proper initialization inside spoolsv.exe, so we can't just load it in an API-Test as usual.
11 * See https://www.reactos.org/pipermail/ros-dev/2015-June/017395.html for more information.
13 * To make testing possible anyway, this program basically does four things:
14 * - Injecting our testing code into spoolsv.exe.
15 * - Registering and running us as a service in the SYSTEM security context like spoolsv.exe, so that injection is possible at all.
16 * - Sending the test name and receiving the console output over named pipes.
17 * - Redirecting the received console output to stdout again, so it looks and feels like a standard API-Test.
19 * To simplify debugging of the injected code, it is entirely separated into a DLL file localspl_apitest.dll.
20 * What we actually inject is a LoadLibraryW call, so that the DLL is loaded gracefully without any hacks.
21 * Therefore, you can just attach your debugger to the spoolsv.exe process and set breakpoints on the localspl_apitest.dll code.
26 #define WIN32_NO_STATUS
37 #include "localspl_apitest.h"
41 _RunRemoteTest(const char* szTestName
)
43 BOOL bSuccessful
= FALSE
;
47 HANDLE hCommandPipe
= INVALID_HANDLE_VALUE
;
49 HANDLE hOutputPipe
= INVALID_HANDLE_VALUE
;
52 SC_HANDLE hService
= NULL
;
53 SERVICE_STATUS ServiceStatus
;
54 WCHAR wszFilePath
[MAX_PATH
+ 20];
57 // Do a dummy EnumPrintersW call.
58 // This guarantees that the Spooler Service has actually loaded localspl.dll, which is a requirement for our injected DLL to work properly.
59 EnumPrintersW(PRINTER_ENUM_LOCAL
| PRINTER_ENUM_NAME
, NULL
, 1, NULL
, 0, &cbRead
, &cbWritten
);
61 // Get the full path to our EXE file.
62 if (!GetModuleFileNameW(NULL
, wszFilePath
, MAX_PATH
))
64 skip("GetModuleFileNameW failed with error %lu!\n", GetLastError());
68 // Replace the extension.
69 p
= wcsrchr(wszFilePath
, L
'.');
72 skip("File path has no file extension: %S\n", wszFilePath
);
78 // Check if the corresponding DLL file exists.
79 hFind
= FindFirstFileW(wszFilePath
, &fd
);
82 skip("My DLL file \"%S\" does not exist!\n", wszFilePath
);
86 // Change the extension back to .exe and add the parameters.
87 wcscpy(p
, L
".exe service dummy");
89 // Open a handle to the service manager.
90 hSC
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
93 skip("OpenSCManagerW failed with error %lu!\n", GetLastError());
97 // Ensure that the spooler service is running.
98 hService
= OpenServiceW(hSC
, L
"spooler", SERVICE_QUERY_STATUS
);
101 skip("OpenServiceW failed for the spooler service with error %lu!\n", GetLastError());
105 if (!QueryServiceStatus(hService
, &ServiceStatus
))
107 skip("QueryServiceStatus failed for the spooler service with error %lu!\n", GetLastError());
111 if (ServiceStatus
.dwCurrentState
!= SERVICE_RUNNING
)
113 skip("Spooler Service is not running!\n");
117 CloseServiceHandle(hService
);
119 // Try to open the service if we've created it in a previous run.
120 hService
= OpenServiceW(hSC
, SERVICE_NAME
, SERVICE_ALL_ACCESS
);
123 if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST
)
125 // Create the service.
126 hService
= CreateServiceW(hSC
, SERVICE_NAME
, NULL
, SERVICE_ALL_ACCESS
, SERVICE_WIN32_OWN_PROCESS
, SERVICE_DEMAND_START
, SERVICE_ERROR_IGNORE
, wszFilePath
, NULL
, NULL
, NULL
, NULL
, NULL
);
129 skip("CreateServiceW failed with error %lu!\n", GetLastError());
135 skip("OpenServiceW failed with error %lu!\n", GetLastError());
140 // Create pipes for the communication with the injected DLL.
141 hCommandPipe
= CreateNamedPipeW(COMMAND_PIPE_NAME
, PIPE_ACCESS_OUTBOUND
, PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
| PIPE_WAIT
, 1, 1024, 1024, 10000, NULL
);
142 if (hCommandPipe
== INVALID_HANDLE_VALUE
)
144 skip("CreateNamedPipeW failed for the command pipe with error %lu!\n", GetLastError());
148 hOutputPipe
= CreateNamedPipeW(OUTPUT_PIPE_NAME
, PIPE_ACCESS_INBOUND
, PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
, 1, 1024, 1024, 10000, NULL
);
149 if (hOutputPipe
== INVALID_HANDLE_VALUE
)
151 skip("CreateNamedPipeW failed for the output pipe with error %lu!\n", GetLastError());
155 // Start the service with "service" and a dummy parameter (to distinguish it from a call by rosautotest to localspl_apitest:service)
156 if (!StartServiceW(hService
, 0, NULL
))
158 skip("StartServiceW failed with error %lu!\n", GetLastError());
162 // Wait till it has injected the DLL and the DLL expects its test name.
163 if (!ConnectNamedPipe(hCommandPipe
, NULL
) && GetLastError() != ERROR_PIPE_CONNECTED
)
165 skip("ConnectNamedPipe failed for the command pipe with error %lu!\n", GetLastError());
169 // Send the test name.
170 if (!WriteFile(hCommandPipe
, szTestName
, strlen(szTestName
) + sizeof(char), &cbWritten
, NULL
))
172 skip("WriteFile failed with error %lu!\n", GetLastError());
176 // Now wait for the DLL to connect to the output pipe.
177 if (!ConnectNamedPipe(hOutputPipe
, NULL
))
179 skip("ConnectNamedPipe failed for the output pipe with error %lu!\n", GetLastError());
183 // Get all testing messages from the pipe and output them on stdout.
184 while (ReadFile(hOutputPipe
, szBuffer
, sizeof(szBuffer
), &cbRead
, NULL
) && cbRead
)
185 fwrite(szBuffer
, sizeof(char), cbRead
, stdout
);
191 CloseHandle(hCommandPipe
);
194 CloseHandle(hOutputPipe
);
200 CloseServiceHandle(hService
);
203 CloseServiceHandle(hSC
);
205 // If we successfully received test output through the named pipe, we have also output a summary line already.
206 // Prevent the testing framework from outputting another "0 tests executed" line in this case.
211 START_TEST(fpEnumPrinters
)
213 _RunRemoteTest("fpEnumPrinters");