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
)
51 WCHAR wszFilePath
[MAX_PATH
+ 20];
54 // Do a dummy EnumPrintersW call.
55 // This guarantees that the Spooler Service has actually loaded localspl.dll, which is a requirement for our injected DLL to work properly.
56 EnumPrintersW(PRINTER_ENUM_LOCAL
| PRINTER_ENUM_NAME
, NULL
, 1, NULL
, 0, &cbRead
, &cbWritten
);
58 // Get the full path to our EXE file.
59 if (!GetModuleFileNameW(NULL
, wszFilePath
, MAX_PATH
))
61 skip("GetModuleFileNameW failed with error %lu!\n", GetLastError());
65 // Replace the extension.
66 p
= wcsrchr(wszFilePath
, L
'.');
69 skip("File path has no file extension: %S\n", wszFilePath
);
75 // Check if the corresponding DLL file exists.
76 if (!FindFirstFileW(wszFilePath
, &fd
))
78 skip("My DLL file \"%S\" does not exist!\n", wszFilePath
);
82 // Change the extension back to .exe and add the parameters.
83 wcscpy(p
, L
".exe service dummy");
85 // Open a handle to the service manager.
86 hSC
= OpenSCManagerW(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
89 skip("OpenSCManagerW failed with error %lu!\n", GetLastError());
93 // Try to open the service if we've created it in a previous run.
94 hService
= OpenServiceW(hSC
, SERVICE_NAME
, SERVICE_ALL_ACCESS
);
97 if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST
)
99 // Create the service.
100 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
);
103 skip("CreateServiceW failed with error %lu!\n", GetLastError());
109 skip("OpenServiceW failed with error %lu!\n", GetLastError());
114 // Create pipes for the communication with the injected DLL.
115 hCommandPipe
= CreateNamedPipeW(COMMAND_PIPE_NAME
, PIPE_ACCESS_OUTBOUND
, PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
| PIPE_WAIT
, 1, 1024, 1024, 10000, NULL
);
116 if (hCommandPipe
== INVALID_HANDLE_VALUE
)
118 skip("CreateNamedPipeW failed for the command pipe with error %lu!\n", GetLastError());
122 hOutputPipe
= CreateNamedPipeW(OUTPUT_PIPE_NAME
, PIPE_ACCESS_INBOUND
, PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
, 1, 1024, 1024, 10000, NULL
);
123 if (hOutputPipe
== INVALID_HANDLE_VALUE
)
125 skip("CreateNamedPipeW failed for the output pipe with error %lu!\n", GetLastError());
129 // Start the service with "service" and a dummy parameter (to distinguish it from a call by rosautotest to localspl_apitest:service)
130 if (!StartServiceW(hService
, 0, NULL
))
132 skip("StartServiceW failed with error %lu!\n", GetLastError());
136 CloseServiceHandle(hService
);
137 CloseServiceHandle(hSC
);
139 // Wait till it has injected the DLL and the DLL expects its test name.
140 if (!ConnectNamedPipe(hCommandPipe
, NULL
) && GetLastError() != ERROR_PIPE_CONNECTED
)
142 skip("ConnectNamedPipe failed for the command pipe with error %lu!\n", GetLastError());
146 // Send the test name.
147 if (!WriteFile(hCommandPipe
, szTestName
, strlen(szTestName
) + sizeof(char), &cbWritten
, NULL
))
149 skip("WriteFile failed with error %lu!\n", GetLastError());
153 CloseHandle(hCommandPipe
);
155 // Now wait for the DLL to connect to the output pipe.
156 if (!ConnectNamedPipe(hOutputPipe
, NULL
))
158 skip("ConnectNamedPipe failed for the output pipe with error %lu!\n", GetLastError());
162 // Get all testing messages from the pipe and output them on stdout.
163 while (ReadFile(hOutputPipe
, szBuffer
, sizeof(szBuffer
), &cbRead
, NULL
) && cbRead
)
164 fwrite(szBuffer
, sizeof(char), cbRead
, stdout
);
166 CloseHandle(hOutputPipe
);
168 // Prevent the testing framework from outputting a "0 tests executed" line here.
172 START_TEST(fpEnumPrinters
)
174 _RunRemoteTest("fpEnumPrinters");