mirror of
https://github.com/reactos/reactos.git
synced 2024-08-28 22:28:36 +00:00
![Colin Finck](/assets/img/avatar_default.png)
- Add more tests for fpEnumPrinters. For some reason, using SEH here works only once. We experience a hang in the testing process when you run the test again for a second time without restarting spoolsv. Needs more investigation. - Ensure that the spooler service is running before starting any testing. - Do proper cleanup in every case. [LOCALSPL] - Implement LocalEnumPrinters level 1 based on the API-Tests. - Use DllAllocSplMem/DllFreeSplMem instead of HeapAlloc/HeapFree. - Use AllocSplStr with DllFreeSplStr now that DuplicateStringW is gone. - Use _countof where applicable. [SPOOLSS] - Found out that I was not the only one needing a wcsdup equivalent. My DuplicateStringW from localspl is actually exported as AllocSplStr in spoolss. This is actually part of a range of undocumented memory functions in spoolss, so implement and document AllocSplStr, DllAllocSplMem, DllFreeSplMem, DllFreeSplStr, ReallocSplMem and ReallocSplStr. Information about some of them was gathered through black box testing and DDK samples (down to Win95 DDK), which at least contained prototypes of them. - Implement SplInitializeWinSpoolDrv based on the API-Test and simply return FALSE for SplIsUpgrade. [SPOOLSS_APITEST] - Add a test for ReallocSplStr, which was actually the most undocumented function of spoolss' memory functions. [WINSPOOL] SplInitializeWinSpoolDrv shows that we can't just auto-assign an ordinal to all winspool.drv functions. We even need to export some nameless functions by ordinal only. Redo the whole .spec file based on the ordinals found in Windows Server 2003's winspool.drv. Trust WINE for the nameless stubs. svn path=/branches/colins-printing-for-freedom/; revision=68089
215 lines
7.2 KiB
C
215 lines
7.2 KiB
C
/*
|
|
* PROJECT: ReactOS Local Spooler API Tests
|
|
* LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
|
|
* PURPOSE: Test list
|
|
* COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
|
|
*/
|
|
|
|
/*
|
|
* The original localspl.dll from Windows Server 2003 is not easily testable.
|
|
* It relies on a proper initialization inside spoolsv.exe, so we can't just load it in an API-Test as usual.
|
|
* See https://www.reactos.org/pipermail/ros-dev/2015-June/017395.html for more information.
|
|
*
|
|
* To make testing possible anyway, this program basically does four things:
|
|
* - Injecting our testing code into spoolsv.exe.
|
|
* - Registering and running us as a service in the SYSTEM security context like spoolsv.exe, so that injection is possible at all.
|
|
* - Sending the test name and receiving the console output over named pipes.
|
|
* - Redirecting the received console output to stdout again, so it looks and feels like a standard API-Test.
|
|
*
|
|
* To simplify debugging of the injected code, it is entirely separated into a DLL file localspl_apitest.dll.
|
|
* What we actually inject is a LoadLibraryW call, so that the DLL is loaded gracefully without any hacks.
|
|
* Therefore, you can just attach your debugger to the spoolsv.exe process and set breakpoints on the localspl_apitest.dll code.
|
|
*/
|
|
|
|
#include <apitest.h>
|
|
|
|
#define WIN32_NO_STATUS
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <wingdi.h>
|
|
#include <winreg.h>
|
|
#include <winsvc.h>
|
|
#include <winspool.h>
|
|
#include <winsplp.h>
|
|
|
|
#include "localspl_apitest.h"
|
|
|
|
|
|
static void
|
|
_RunRemoteTest(const char* szTestName)
|
|
{
|
|
BOOL bSuccessful = FALSE;
|
|
char szBuffer[1024];
|
|
DWORD cbRead;
|
|
DWORD cbWritten;
|
|
HANDLE hCommandPipe = INVALID_HANDLE_VALUE;
|
|
HANDLE hFind = NULL;
|
|
HANDLE hOutputPipe = INVALID_HANDLE_VALUE;
|
|
PWSTR p;
|
|
SC_HANDLE hSC = NULL;
|
|
SC_HANDLE hService = NULL;
|
|
SERVICE_STATUS ServiceStatus;
|
|
WCHAR wszFilePath[MAX_PATH + 20];
|
|
WIN32_FIND_DATAW fd;
|
|
|
|
// Do a dummy EnumPrintersW call.
|
|
// This guarantees that the Spooler Service has actually loaded localspl.dll, which is a requirement for our injected DLL to work properly.
|
|
EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbRead, &cbWritten);
|
|
|
|
// Get the full path to our EXE file.
|
|
if (!GetModuleFileNameW(NULL, wszFilePath, MAX_PATH))
|
|
{
|
|
skip("GetModuleFileNameW failed with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Replace the extension.
|
|
p = wcsrchr(wszFilePath, L'.');
|
|
if (!p)
|
|
{
|
|
skip("File path has no file extension: %S\n", wszFilePath);
|
|
goto Cleanup;
|
|
}
|
|
|
|
wcscpy(p, L".dll");
|
|
|
|
// Check if the corresponding DLL file exists.
|
|
hFind = FindFirstFileW(wszFilePath, &fd);
|
|
if (!hFind)
|
|
{
|
|
skip("My DLL file \"%S\" does not exist!\n", wszFilePath);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Change the extension back to .exe and add the parameters.
|
|
wcscpy(p, L".exe service dummy");
|
|
|
|
// Open a handle to the service manager.
|
|
hSC = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
|
if (!hSC)
|
|
{
|
|
skip("OpenSCManagerW failed with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Ensure that the spooler service is running.
|
|
hService = OpenServiceW(hSC, L"spooler", SERVICE_QUERY_STATUS);
|
|
if (!hService)
|
|
{
|
|
skip("OpenServiceW failed for the spooler service with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!QueryServiceStatus(hService, &ServiceStatus))
|
|
{
|
|
skip("QueryServiceStatus failed for the spooler service with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (ServiceStatus.dwCurrentState != SERVICE_RUNNING)
|
|
{
|
|
skip("Spooler Service is not running!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
CloseServiceHandle(hService);
|
|
|
|
// Try to open the service if we've created it in a previous run.
|
|
hService = OpenServiceW(hSC, SERVICE_NAME, SERVICE_ALL_ACCESS);
|
|
if (!hService)
|
|
{
|
|
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
|
|
{
|
|
// Create the service.
|
|
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);
|
|
if (!hService)
|
|
{
|
|
skip("CreateServiceW failed with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
skip("OpenServiceW failed with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
// Create pipes for the communication with the injected DLL.
|
|
hCommandPipe = CreateNamedPipeW(COMMAND_PIPE_NAME, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 1024, 1024, 10000, NULL);
|
|
if (hCommandPipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
skip("CreateNamedPipeW failed for the command pipe with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
hOutputPipe = CreateNamedPipeW(OUTPUT_PIPE_NAME, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 10000, NULL);
|
|
if (hOutputPipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
skip("CreateNamedPipeW failed for the output pipe with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Start the service with "service" and a dummy parameter (to distinguish it from a call by rosautotest to localspl_apitest:service)
|
|
if (!StartServiceW(hService, 0, NULL))
|
|
{
|
|
skip("StartServiceW failed with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Wait till it has injected the DLL and the DLL expects its test name.
|
|
if (!ConnectNamedPipe(hCommandPipe, NULL) && GetLastError() != ERROR_PIPE_CONNECTED)
|
|
{
|
|
skip("ConnectNamedPipe failed for the command pipe with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Send the test name.
|
|
if (!WriteFile(hCommandPipe, szTestName, strlen(szTestName) + sizeof(char), &cbWritten, NULL))
|
|
{
|
|
skip("WriteFile failed with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Now wait for the DLL to connect to the output pipe.
|
|
if (!ConnectNamedPipe(hOutputPipe, NULL))
|
|
{
|
|
skip("ConnectNamedPipe failed for the output pipe with error %lu!\n", GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Get all testing messages from the pipe and output them on stdout.
|
|
while (ReadFile(hOutputPipe, szBuffer, sizeof(szBuffer), &cbRead, NULL) && cbRead)
|
|
fwrite(szBuffer, sizeof(char), cbRead, stdout);
|
|
|
|
bSuccessful = TRUE;
|
|
|
|
Cleanup:
|
|
if (hCommandPipe)
|
|
CloseHandle(hCommandPipe);
|
|
|
|
if (hOutputPipe)
|
|
CloseHandle(hOutputPipe);
|
|
|
|
if (hFind)
|
|
FindClose(hFind);
|
|
|
|
if (hService)
|
|
CloseServiceHandle(hService);
|
|
|
|
if (hSC)
|
|
CloseServiceHandle(hSC);
|
|
|
|
// If we successfully received test output through the named pipe, we have also output a summary line already.
|
|
// Prevent the testing framework from outputting another "0 tests executed" line in this case.
|
|
if (bSuccessful)
|
|
ExitProcess(0);
|
|
}
|
|
|
|
START_TEST(fpEnumPrinters)
|
|
{
|
|
_RunRemoteTest("fpEnumPrinters");
|
|
}
|