mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 05:22:57 +00:00
[KMTEST] Initial usermode support for testing FS mini-filters (#81)
* [KMTEST] Initial usermode support for testing FS mini-filters - Add base routines to wrap the win32 'Filter' APis - Add support routines to be used when testing FS filter drivers - Move KmtCreateService to a private routine so it can be shared with KmtFltCreateService - Completely untested at the mo, so likely contains bugs at this point
This commit is contained in:
parent
5cfc1e3152
commit
b10dd06aa5
8 changed files with 882 additions and 31 deletions
|
@ -119,6 +119,8 @@ add_target_include_directories(kmtest_printf ${REACTOS_SOURCE_DIR}/sdk/lib/crt/i
|
||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND KMTEST_SOURCE
|
list(APPEND KMTEST_SOURCE
|
||||||
|
kmtest/filter.c
|
||||||
|
kmtest/fltsupport.c
|
||||||
kmtest/kmtest.c
|
kmtest/kmtest.c
|
||||||
kmtest/service.c
|
kmtest/service.c
|
||||||
kmtest/support.c
|
kmtest/support.c
|
||||||
|
@ -143,8 +145,8 @@ list(APPEND KMTEST_SOURCE
|
||||||
add_executable(kmtest ${KMTEST_SOURCE})
|
add_executable(kmtest ${KMTEST_SOURCE})
|
||||||
set_module_type(kmtest win32cui)
|
set_module_type(kmtest win32cui)
|
||||||
target_link_libraries(kmtest ${PSEH_LIB})
|
target_link_libraries(kmtest ${PSEH_LIB})
|
||||||
add_importlibs(kmtest advapi32 ws2_32 msvcrt kernel32 ntdll)
|
add_importlibs(kmtest fltlib advapi32 ws2_32 msvcrt kernel32 ntdll)
|
||||||
add_target_compile_definitions(kmtest KMT_USER_MODE)
|
add_target_compile_definitions(kmtest KMT_USER_MODE NTDDI_VERSION=NTDDI_WS03SP1)
|
||||||
#add_pch(kmtest include/kmt_test.h)
|
#add_pch(kmtest include/kmt_test.h)
|
||||||
set_target_properties(kmtest PROPERTIES OUTPUT_NAME "kmtest_")
|
set_target_properties(kmtest PROPERTIES OUTPUT_NAME "kmtest_")
|
||||||
#add_rostests_file(TARGET kmtest)
|
#add_rostests_file(TARGET kmtest)
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include <ndk/rtlfuncs.h>
|
#include <ndk/rtlfuncs.h>
|
||||||
#include <ndk/mmfuncs.h>
|
#include <ndk/mmfuncs.h>
|
||||||
#include <strsafe.h>
|
#include <strsafe.h>
|
||||||
|
#include <fltuser.h>
|
||||||
|
|
||||||
#ifdef KMT_EMULATE_KERNEL
|
#ifdef KMT_EMULATE_KERNEL
|
||||||
#define ok_irql(i)
|
#define ok_irql(i)
|
||||||
|
|
|
@ -23,6 +23,11 @@
|
||||||
#define IOCTL_KMTEST_USERMODE_AWAIT_REQ \
|
#define IOCTL_KMTEST_USERMODE_AWAIT_REQ \
|
||||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_READ_DATA)
|
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_READ_DATA)
|
||||||
|
|
||||||
|
|
||||||
|
#define KMTFLT_GET_TESTS 0x800
|
||||||
|
#define KMTFLT_RUN_TEST 0x801
|
||||||
|
|
||||||
|
|
||||||
#define KMTEST_DEVICE_NAME L"Kmtest"
|
#define KMTEST_DEVICE_NAME L"Kmtest"
|
||||||
#define KMTEST_DEVICE_DRIVER_PATH L"\\Device\\" KMTEST_DEVICE_NAME
|
#define KMTEST_DEVICE_DRIVER_PATH L"\\Device\\" KMTEST_DEVICE_NAME
|
||||||
#define KMTEST_DEVICE_PATH L"\\\\.\\Global\\GLOBALROOT" KMTEST_DEVICE_DRIVER_PATH
|
#define KMTEST_DEVICE_PATH L"\\\\.\\Global\\GLOBALROOT" KMTEST_DEVICE_DRIVER_PATH
|
||||||
|
|
|
@ -173,6 +173,16 @@ DWORD KmtSendStringToDriver(IN DWORD ControlCode, IN PCSTR String);
|
||||||
DWORD KmtSendWStringToDriver(IN DWORD ControlCode, IN PCWSTR String);
|
DWORD KmtSendWStringToDriver(IN DWORD ControlCode, IN PCWSTR String);
|
||||||
DWORD KmtSendUlongToDriver(IN DWORD ControlCode, IN DWORD Value);
|
DWORD KmtSendUlongToDriver(IN DWORD ControlCode, IN DWORD Value);
|
||||||
DWORD KmtSendBufferToDriver(IN DWORD ControlCode, IN OUT PVOID Buffer OPTIONAL, IN DWORD InLength, IN OUT PDWORD OutLength);
|
DWORD KmtSendBufferToDriver(IN DWORD ControlCode, IN OUT PVOID Buffer OPTIONAL, IN DWORD InLength, IN OUT PDWORD OutLength);
|
||||||
|
|
||||||
|
DWORD KmtFltLoadDriver(_In_z_ PCWSTR ServiceName, _In_ BOOLEAN RestartIfRunning, _In_ BOOLEAN ConnectComms, _Out_ HANDLE *hPort);
|
||||||
|
DWORD KmtFltUnloadDriver(_In_ HANDLE *hPort, _In_ BOOLEAN DisonnectComms);
|
||||||
|
DWORD KmtFltRunKernelTest(_In_ HANDLE hPort, _In_z_ PCSTR TestName);
|
||||||
|
DWORD KmtFltSendToDriver(_In_ HANDLE hPort, _In_ DWORD Message);
|
||||||
|
DWORD KmtFltSendStringToDriver(_In_ HANDLE hPort, _In_ DWORD Message, _In_ PCSTR String);
|
||||||
|
DWORD KmtFltSendWStringToDriver(_In_ HANDLE hPort, _In_ DWORD Message, _In_ PCWSTR String);
|
||||||
|
DWORD KmtFltSendUlongToDriver(_In_ HANDLE hPort, _In_ DWORD Message, _In_ DWORD Value);
|
||||||
|
DWORD KmtFltSendBufferToDriver(_In_ HANDLE hPort, _In_ DWORD Message, _In_reads_bytes_(BufferSize) LPVOID Buffer, _In_ DWORD BufferSize, _Out_writes_bytes_to_opt_(dwOutBufferSize, *lpBytesReturned) LPVOID lpOutBuffer, _In_ DWORD dwOutBufferSize, _Out_opt_ LPDWORD lpBytesReturned);
|
||||||
|
|
||||||
#else /* if !defined KMT_KERNEL_MODE && !defined KMT_USER_MODE */
|
#else /* if !defined KMT_KERNEL_MODE && !defined KMT_USER_MODE */
|
||||||
#error either KMT_KERNEL_MODE or KMT_USER_MODE must be defined
|
#error either KMT_KERNEL_MODE or KMT_USER_MODE must be defined
|
||||||
#endif /* !defined KMT_KERNEL_MODE && !defined KMT_USER_MODE */
|
#endif /* !defined KMT_KERNEL_MODE && !defined KMT_USER_MODE */
|
||||||
|
|
428
modules/rostests/kmtests/kmtest/filter.c
Normal file
428
modules/rostests/kmtests/kmtest/filter.c
Normal file
|
@ -0,0 +1,428 @@
|
||||||
|
/*
|
||||||
|
* PROJECT: ReactOS kernel-mode tests
|
||||||
|
* LICENSE: GPLv2+ - See COPYING in the top level directory
|
||||||
|
* PURPOSE: File system filter implementation of the original service.c file
|
||||||
|
* PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
|
||||||
|
* Ged Murphy <gedmurphy@reactos.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#include <fltuser.h>
|
||||||
|
#include <kmt_test.h>
|
||||||
|
#include "kmtest.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define SERVICE_ACCESS (SERVICE_START | SERVICE_STOP | DELETE)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to call the internal function in the service.c file
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtpCreateService(
|
||||||
|
IN PCWSTR ServiceName,
|
||||||
|
IN PCWSTR ServicePath,
|
||||||
|
IN PCWSTR DisplayName OPTIONAL,
|
||||||
|
IN DWORD ServiceType,
|
||||||
|
OUT SC_HANDLE *ServiceHandle);
|
||||||
|
|
||||||
|
static SC_HANDLE ScmHandle;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltCreateService
|
||||||
|
*
|
||||||
|
* Create the specified driver service and return a handle to it
|
||||||
|
*
|
||||||
|
* @param ServiceName
|
||||||
|
* Name of the service to create
|
||||||
|
* @param ServicePath
|
||||||
|
* File name of the driver, relative to the current directory
|
||||||
|
* @param DisplayName
|
||||||
|
* Service display name
|
||||||
|
* @param ServiceHandle
|
||||||
|
* Pointer to a variable to receive the handle to the service
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltCreateService(
|
||||||
|
_In_z_ PCWSTR ServiceName,
|
||||||
|
_In_z_ PCWSTR ServicePath,
|
||||||
|
_In_z_ PCWSTR DisplayName OPTIONAL,
|
||||||
|
_Out_ SC_HANDLE *ServiceHandle)
|
||||||
|
{
|
||||||
|
return KmtpCreateService(ServiceName,
|
||||||
|
ServicePath,
|
||||||
|
DisplayName,
|
||||||
|
SERVICE_FILE_SYSTEM_DRIVER,
|
||||||
|
ServiceHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltLoad
|
||||||
|
*
|
||||||
|
* Start the specified filter driver by name
|
||||||
|
*
|
||||||
|
* @param ServiceName
|
||||||
|
* The name of the filter to start
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltLoad(
|
||||||
|
_In_z_ PCWSTR ServiceName)
|
||||||
|
{
|
||||||
|
HRESULT hResult;
|
||||||
|
DWORD Error = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
assert(ServiceName);
|
||||||
|
|
||||||
|
hResult = FilterLoad(ServiceName);
|
||||||
|
Error = SCODE_CODE(hResult);
|
||||||
|
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltCreateAndStartService
|
||||||
|
*
|
||||||
|
* Create and load the specified filter driver and return a handle to it
|
||||||
|
*
|
||||||
|
* @param ServiceName
|
||||||
|
* Name of the service to create
|
||||||
|
* @param ServicePath
|
||||||
|
* File name of the driver, relative to the current directory
|
||||||
|
* @param DisplayName
|
||||||
|
* Service display name
|
||||||
|
* @param ServiceHandle
|
||||||
|
* Pointer to a variable to receive the handle to the service
|
||||||
|
* @param RestartIfRunning
|
||||||
|
* TRUE to stop and restart the service if it is already running
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltCreateAndStartService(
|
||||||
|
_In_z_ PCWSTR ServiceName,
|
||||||
|
_In_z_ PCWSTR ServicePath,
|
||||||
|
_In_z_ PCWSTR DisplayName OPTIONAL,
|
||||||
|
_Out_ SC_HANDLE *ServiceHandle,
|
||||||
|
_In_ BOOLEAN RestartIfRunning)
|
||||||
|
{
|
||||||
|
DWORD Error = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
assert(ServiceHandle);
|
||||||
|
|
||||||
|
Error = KmtFltCreateService(ServiceName, ServicePath, DisplayName, ServiceHandle);
|
||||||
|
|
||||||
|
if (Error == ERROR_SERVICE_EXISTS)
|
||||||
|
*ServiceHandle = OpenService(ScmHandle, ServiceName, SERVICE_ACCESS);
|
||||||
|
|
||||||
|
if (Error && Error != ERROR_SERVICE_EXISTS)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
Error = KmtFltLoad(ServiceName);
|
||||||
|
|
||||||
|
if (Error != ERROR_SERVICE_ALREADY_RUNNING)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
Error = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (!RestartIfRunning)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
Error = KmtFltUnload(ServiceName);
|
||||||
|
if (Error)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
Error = KmtFltLoad(ServiceName);
|
||||||
|
if (Error)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
assert(Error);
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltConnect
|
||||||
|
*
|
||||||
|
* Create a comms connection to the specified filter
|
||||||
|
*
|
||||||
|
* @param ServiceName
|
||||||
|
* Name of the filter to connect to
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltConnect(
|
||||||
|
_In_z_ PCWSTR ServiceName,
|
||||||
|
_Out_ HANDLE *hPort)
|
||||||
|
{
|
||||||
|
HRESULT hResult;
|
||||||
|
DWORD Error = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
assert(ServiceName);
|
||||||
|
assert(hPort);
|
||||||
|
|
||||||
|
hResult = FilterConnectCommunicationPort(ServiceName,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
hPort);
|
||||||
|
Error = SCODE_CODE(hResult);
|
||||||
|
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltDisconnect
|
||||||
|
*
|
||||||
|
* Disconenct from the comms port
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltDisconnect(
|
||||||
|
_Out_ HANDLE *hPort)
|
||||||
|
{
|
||||||
|
DWORD Error = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
assert(hPort);
|
||||||
|
|
||||||
|
if (!CloseHandle(hPort))
|
||||||
|
{
|
||||||
|
Error = GetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltSendMessage
|
||||||
|
*
|
||||||
|
* Sneds a message to a filter driver
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @InBuffer
|
||||||
|
* Pointer to a buffer to send to the filter
|
||||||
|
* @InBufferSize
|
||||||
|
* Size of the buffer pointed to by InBuffer
|
||||||
|
* @OutBuffer
|
||||||
|
* Pointer to a buffer to receive reply data from the filter
|
||||||
|
* @OutBufferSize
|
||||||
|
* Size of the buffer pointed to by OutBuffer
|
||||||
|
* @BytesReturned
|
||||||
|
* Number of bytes written in the reply buffer
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltSendMessage(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_reads_bytes_(dwInBufferSize) LPVOID InBuffer,
|
||||||
|
_In_ DWORD InBufferSize,
|
||||||
|
_Out_writes_bytes_to_opt_(dutBufferSize, *BytesReturned) LPVOID OutBuffer,
|
||||||
|
_In_ DWORD OutBufferSize,
|
||||||
|
_Out_opt_ LPDWORD BytesReturned)
|
||||||
|
{
|
||||||
|
DWORD BytesRet;
|
||||||
|
HRESULT hResult;
|
||||||
|
DWORD Error;
|
||||||
|
|
||||||
|
assert(hPort);
|
||||||
|
assert(InBuffer);
|
||||||
|
assert(InBufferSize);
|
||||||
|
|
||||||
|
if (BytesReturned) *BytesReturned = 0;
|
||||||
|
|
||||||
|
hResult = FilterSendMessage(hPort,
|
||||||
|
InBuffer,
|
||||||
|
InBufferSize,
|
||||||
|
OutBuffer,
|
||||||
|
OutBufferSize,
|
||||||
|
&BytesRet);
|
||||||
|
|
||||||
|
Error = SCODE_CODE(hResult);
|
||||||
|
if (Error == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (BytesRet)
|
||||||
|
{
|
||||||
|
*BytesReturned = BytesRet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltGetMessage
|
||||||
|
*
|
||||||
|
* Gets a message from a filter driver
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @MessageBuffer
|
||||||
|
* Pointer to a buffer to receive the data from the filter
|
||||||
|
* @MessageBufferSize
|
||||||
|
* Size of the buffer pointed to by MessageBuffer
|
||||||
|
* @Overlapped
|
||||||
|
* Pointer to an overlapped structure
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltGetMessage(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_Out_writes_bytes_(MessageBufferSize) PFILTER_MESSAGE_HEADER MessageBuffer,
|
||||||
|
_In_ DWORD MessageBufferSize,
|
||||||
|
_In_opt_ LPOVERLAPPED Overlapped)
|
||||||
|
{
|
||||||
|
HRESULT hResult;
|
||||||
|
DWORD Error;
|
||||||
|
|
||||||
|
assert(hPort);
|
||||||
|
assert(MessageBuffer);
|
||||||
|
|
||||||
|
hResult = FilterGetMessage(hPort,
|
||||||
|
MessageBuffer,
|
||||||
|
MessageBufferSize,
|
||||||
|
Overlapped);
|
||||||
|
Error = SCODE_CODE(hResult);
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltReplyMessage
|
||||||
|
*
|
||||||
|
* Replies to a message from a filter driver
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @ReplyBuffer
|
||||||
|
* Pointer to a buffer to return to the filter
|
||||||
|
* @ReplyBufferSize
|
||||||
|
* Size of the buffer pointed to by ReplyBuffer
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltReplyMessage(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_reads_bytes_(ReplyBufferSize) PFILTER_REPLY_HEADER ReplyBuffer,
|
||||||
|
_In_ DWORD ReplyBufferSize)
|
||||||
|
{
|
||||||
|
HRESULT hResult;
|
||||||
|
DWORD Error;
|
||||||
|
|
||||||
|
hResult = FilterReplyMessage(hPort,
|
||||||
|
ReplyBuffer,
|
||||||
|
ReplyBufferSize);
|
||||||
|
Error = SCODE_CODE(hResult);
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltGetMessageResult
|
||||||
|
*
|
||||||
|
* Gets the overlapped result from the IO
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @Overlapped
|
||||||
|
* Pointer to the overlapped structure usdd in the IO
|
||||||
|
* @BytesTransferred
|
||||||
|
* Number of bytes transferred in the IO
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltGetMessageResult(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_ LPOVERLAPPED Overlapped,
|
||||||
|
_Out_ LPDWORD BytesTransferred)
|
||||||
|
{
|
||||||
|
BOOL Success;
|
||||||
|
DWORD Error = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
*BytesTransferred = 0;
|
||||||
|
|
||||||
|
Success = GetOverlappedResult(hPort, Overlapped, BytesTransferred, TRUE);
|
||||||
|
if (!Success)
|
||||||
|
{
|
||||||
|
Error = GetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltUnload
|
||||||
|
*
|
||||||
|
* Unload the specified filter driver
|
||||||
|
*
|
||||||
|
* @param ServiceName
|
||||||
|
* The name of the filter to unload
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltUnload(
|
||||||
|
_In_z_ PCWSTR ServiceName)
|
||||||
|
{
|
||||||
|
HRESULT hResult;
|
||||||
|
DWORD Error = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
assert(ServiceName);
|
||||||
|
|
||||||
|
hResult = FilterUnload(ServiceName);
|
||||||
|
Error = SCODE_CODE(hResult);
|
||||||
|
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltDeleteService
|
||||||
|
*
|
||||||
|
* Delete the specified filter driver
|
||||||
|
*
|
||||||
|
* @param ServiceName
|
||||||
|
* If *ServiceHandle is NULL, name of the service to delete
|
||||||
|
* @param ServiceHandle
|
||||||
|
* Pointer to a variable containing the service handle.
|
||||||
|
* Will be set to NULL on success
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltDeleteService(
|
||||||
|
_In_z_ PCWSTR ServiceName OPTIONAL,
|
||||||
|
_Inout_ SC_HANDLE *ServiceHandle)
|
||||||
|
{
|
||||||
|
return KmtDeleteService(ServiceName, ServiceHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltCloseService
|
||||||
|
*
|
||||||
|
* Close the specified driver service handle
|
||||||
|
*
|
||||||
|
* @param ServiceHandle
|
||||||
|
* Pointer to a variable containing the service handle.
|
||||||
|
* Will be set to NULL on success
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD KmtFltCloseService(
|
||||||
|
_Inout_ SC_HANDLE *ServiceHandle)
|
||||||
|
{
|
||||||
|
return KmtCloseService(ServiceHandle);
|
||||||
|
}
|
301
modules/rostests/kmtests/kmtest/fltsupport.c
Normal file
301
modules/rostests/kmtests/kmtest/fltsupport.c
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
/*
|
||||||
|
* PROJECT: ReactOS kernel-mode tests
|
||||||
|
* LICENSE: GPLv2+ - See COPYING in the top level directory
|
||||||
|
* PURPOSE: File system mini-filter support routines
|
||||||
|
* PROGRAMMER: Ged Murphy <gedmurphy@reactos.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kmt_test.h>
|
||||||
|
|
||||||
|
#include "kmtest.h"
|
||||||
|
#include <kmt_public.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <debug.h>
|
||||||
|
|
||||||
|
// move to a shared location
|
||||||
|
typedef struct _KMTFLT_MESSAGE_HEADER
|
||||||
|
{
|
||||||
|
ULONG Message;
|
||||||
|
PVOID Buffer;
|
||||||
|
ULONG BufferSize;
|
||||||
|
|
||||||
|
} KMTFLT_MESSAGE_HEADER, *PKMTFLT_MESSAGE_HEADER;
|
||||||
|
|
||||||
|
extern HANDLE KmtestHandle;
|
||||||
|
static WCHAR TestServiceName[MAX_PATH];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltLoadDriver
|
||||||
|
*
|
||||||
|
* Load the specified filter driver
|
||||||
|
* This routine will create the service entry if it doesn't already exist
|
||||||
|
*
|
||||||
|
* @param ServiceName
|
||||||
|
* Name of the driver service (Kmtest- prefix will be added automatically)
|
||||||
|
* @param RestartIfRunning
|
||||||
|
* TRUE to stop and restart the service if it is already running
|
||||||
|
* @param ConnectComms
|
||||||
|
* TRUE to create a comms connection to the specified filter
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltLoadDriver(
|
||||||
|
_In_z_ PCWSTR ServiceName,
|
||||||
|
_In_ BOOLEAN RestartIfRunning,
|
||||||
|
_In_ BOOLEAN ConnectComms,
|
||||||
|
_Out_ HANDLE *hPort)
|
||||||
|
{
|
||||||
|
DWORD Error = ERROR_SUCCESS;
|
||||||
|
WCHAR ServicePath[MAX_PATH];
|
||||||
|
SC_HANDLE TestServiceHandle;
|
||||||
|
|
||||||
|
StringCbCopy(ServicePath, sizeof ServicePath, ServiceName);
|
||||||
|
StringCbCat(ServicePath, sizeof ServicePath, L"_drv.sys");
|
||||||
|
|
||||||
|
StringCbCopy(TestServiceName, sizeof TestServiceName, L"Kmtest-");
|
||||||
|
StringCbCat(TestServiceName, sizeof TestServiceName, ServiceName);
|
||||||
|
|
||||||
|
Error = KmtFltCreateAndStartService(TestServiceName, ServicePath, NULL, &TestServiceHandle, TRUE);
|
||||||
|
|
||||||
|
if (Error == ERROR_SUCCESS && ConnectComms)
|
||||||
|
{
|
||||||
|
Error = KmtFltConnect(ServiceName, hPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltUnloadDriver
|
||||||
|
*
|
||||||
|
* Unload the specified filter driver
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @param ConnectComms
|
||||||
|
* TRUE to disconnect the comms connection before unloading
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltUnloadDriver(
|
||||||
|
_In_ HANDLE *hPort,
|
||||||
|
_In_ BOOLEAN DisonnectComms)
|
||||||
|
{
|
||||||
|
DWORD Error = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (DisonnectComms)
|
||||||
|
{
|
||||||
|
Error = KmtFltDisconnect(hPort);
|
||||||
|
|
||||||
|
if (Error)
|
||||||
|
{
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Error = KmtFltUnload(TestServiceName);
|
||||||
|
|
||||||
|
if (Error)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
__debugbreak();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltRunKernelTest
|
||||||
|
*
|
||||||
|
* Run the specified filter test part
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @param TestName
|
||||||
|
* Name of the test to run
|
||||||
|
*
|
||||||
|
* @return Win32 error code
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltRunKernelTest(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_z_ PCSTR TestName)
|
||||||
|
{
|
||||||
|
return KmtFltSendStringToDriver(hPort, KMTFLT_RUN_TEST, TestName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltSendToDriver
|
||||||
|
*
|
||||||
|
* Send an I/O control message with no arguments to the driver opened with KmtOpenDriver
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @param Message
|
||||||
|
* The message to send to the filter
|
||||||
|
*
|
||||||
|
* @return Win32 error code as returned by DeviceIoControl
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltSendToDriver(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_ DWORD Message)
|
||||||
|
{
|
||||||
|
assert(hPort);
|
||||||
|
return KmtFltSendBufferToDriver(hPort, Message, NULL, 0, NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltSendStringToDriver
|
||||||
|
*
|
||||||
|
* Send an I/O control message with a string argument to the driver opened with KmtOpenDriver
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @param Message
|
||||||
|
* The message associated with the string
|
||||||
|
* @param String
|
||||||
|
* An ANSI string to send to the filter
|
||||||
|
*
|
||||||
|
* @return Win32 error code as returned by DeviceIoControl
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltSendStringToDriver(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_ DWORD Message,
|
||||||
|
_In_ PCSTR String)
|
||||||
|
{
|
||||||
|
assert(hPort);
|
||||||
|
assert(String);
|
||||||
|
return KmtFltSendBufferToDriver(hPort, Message, (PVOID)String, (DWORD)strlen(String), NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltSendWStringToDriver
|
||||||
|
*
|
||||||
|
* Send an I/O control message with a wide string argument to the driver opened with KmtOpenDriver
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @param Message
|
||||||
|
* The message associated with the string
|
||||||
|
* @param String
|
||||||
|
* An wide string to send to the filter
|
||||||
|
*
|
||||||
|
* @return Win32 error code as returned by DeviceIoControl
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltSendWStringToDriver(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_ DWORD Message,
|
||||||
|
_In_ PCWSTR String)
|
||||||
|
{
|
||||||
|
return KmtFltSendBufferToDriver(hPort, Message, (PVOID)String, (DWORD)wcslen(String) * sizeof(WCHAR), NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtFltSendUlongToDriver
|
||||||
|
*
|
||||||
|
* Send an I/O control message with an integer argument to the driver opened with KmtOpenDriver
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @param Message
|
||||||
|
* The message associated with the value
|
||||||
|
* @param Value
|
||||||
|
* An 32bit valueng to send to the filter
|
||||||
|
*
|
||||||
|
* @return Win32 error code as returned by DeviceIoControl
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltSendUlongToDriver(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_ DWORD Message,
|
||||||
|
_In_ DWORD Value)
|
||||||
|
{
|
||||||
|
return KmtFltSendBufferToDriver(hPort, Message, &Value, sizeof(Value), NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name KmtSendBufferToDriver
|
||||||
|
*
|
||||||
|
* Send an I/O control message with the specified arguments to the driver opened with KmtOpenDriver
|
||||||
|
*
|
||||||
|
* @param hPort
|
||||||
|
* Handle to the filter's comms port
|
||||||
|
* @param Message
|
||||||
|
* The message associated with the value
|
||||||
|
* @param InBuffer
|
||||||
|
* Pointer to a buffer to send to the filter
|
||||||
|
* @param BufferSize
|
||||||
|
* Size of the buffer pointed to by InBuffer
|
||||||
|
* @param OutBuffer
|
||||||
|
* Pointer to a buffer to receive a response from the filter
|
||||||
|
* @param OutBufferSize
|
||||||
|
* Size of the buffer pointed to by OutBuffer
|
||||||
|
* @param BytesReturned
|
||||||
|
* Number of bytes written in the reply buffer
|
||||||
|
*
|
||||||
|
* @return Win32 error code as returned by DeviceIoControl
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtFltSendBufferToDriver(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_ DWORD Message,
|
||||||
|
_In_reads_bytes_(BufferSize) LPVOID InBuffer,
|
||||||
|
_In_ DWORD BufferSize,
|
||||||
|
_Out_writes_bytes_to_opt_(OutBufferSize, *BytesReturned) LPVOID OutBuffer,
|
||||||
|
_In_ DWORD OutBufferSize,
|
||||||
|
_Out_opt_ LPDWORD BytesReturned)
|
||||||
|
{
|
||||||
|
PKMTFLT_MESSAGE_HEADER Ptr;
|
||||||
|
KMTFLT_MESSAGE_HEADER Header;
|
||||||
|
BOOLEAN FreeMemory = FALSE;
|
||||||
|
DWORD InBufferSize;
|
||||||
|
DWORD Error;
|
||||||
|
|
||||||
|
assert(hPort);
|
||||||
|
|
||||||
|
if (BufferSize)
|
||||||
|
{
|
||||||
|
assert(InBuffer);
|
||||||
|
|
||||||
|
InBufferSize = sizeof(KMTFLT_MESSAGE_HEADER) + BufferSize;
|
||||||
|
Ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, InBufferSize);
|
||||||
|
if (!Ptr)
|
||||||
|
{
|
||||||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
}
|
||||||
|
FreeMemory = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InBufferSize = sizeof(KMTFLT_MESSAGE_HEADER);
|
||||||
|
Ptr = &Header;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr->Message = Message;
|
||||||
|
if (BufferSize)
|
||||||
|
{
|
||||||
|
Ptr->Buffer = (Ptr + 1);
|
||||||
|
StringCbCopy(Ptr->Buffer, BufferSize, InBuffer);
|
||||||
|
Ptr->BufferSize = BufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error = KmtFltSendMessage(hPort, Ptr, InBufferSize, OutBuffer, OutBufferSize, BytesReturned);
|
||||||
|
|
||||||
|
if (FreeMemory)
|
||||||
|
{
|
||||||
|
HeapFree(GetProcessHeap(), 0, Ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Error;
|
||||||
|
}
|
|
@ -61,4 +61,75 @@ KmtDeleteService(
|
||||||
DWORD KmtCloseService(
|
DWORD KmtCloseService(
|
||||||
IN OUT SC_HANDLE *ServiceHandle);
|
IN OUT SC_HANDLE *ServiceHandle);
|
||||||
|
|
||||||
|
|
||||||
|
/* FS Filter management functions */
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltCreateService(
|
||||||
|
_In_z_ PCWSTR ServiceName,
|
||||||
|
_In_z_ PCWSTR ServicePath,
|
||||||
|
_In_z_ PCWSTR DisplayName OPTIONAL,
|
||||||
|
_Out_ SC_HANDLE *ServiceHandle);
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltLoad(
|
||||||
|
_In_z_ PCWSTR ServiceName);
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltCreateAndStartService(
|
||||||
|
_In_z_ PCWSTR ServiceName,
|
||||||
|
_In_z_ PCWSTR ServicePath,
|
||||||
|
_In_z_ PCWSTR DisplayName OPTIONAL,
|
||||||
|
_Out_ SC_HANDLE *ServiceHandle,
|
||||||
|
_In_ BOOLEAN RestartIfRunning);
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltConnect(
|
||||||
|
_In_z_ PCWSTR ServiceName,
|
||||||
|
_Out_ HANDLE *hPort);
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltDisconnect(
|
||||||
|
_Out_ HANDLE *hPort);
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltSendMessage(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_reads_bytes_(dwInBufferSize) LPVOID lpInBuffer,
|
||||||
|
_In_ DWORD dwInBufferSize,
|
||||||
|
_Out_writes_bytes_to_opt_(dwOutBufferSize, *lpBytesReturned) LPVOID lpOutBuffer,
|
||||||
|
_In_ DWORD dwOutBufferSize,
|
||||||
|
_Out_opt_ LPDWORD lpBytesReturned);
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltGetMessage(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_Out_writes_bytes_(dwMessageBufferSize) PFILTER_MESSAGE_HEADER lpMessageBuffer,
|
||||||
|
_In_ DWORD dwMessageBufferSize,
|
||||||
|
_In_opt_ LPOVERLAPPED Overlapped);
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltReplyMessage(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_reads_bytes_(dwReplyBufferSize) PFILTER_REPLY_HEADER lpReplyBuffer,
|
||||||
|
_In_ DWORD dwReplyBufferSize);
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltGetMessageResult(
|
||||||
|
_In_ HANDLE hPort,
|
||||||
|
_In_ LPOVERLAPPED Overlapped,
|
||||||
|
_Out_ LPDWORD BytesTransferred);
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltUnload(
|
||||||
|
_In_z_ PCWSTR ServiceName);
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
KmtFltDeleteService(
|
||||||
|
_In_z_ PCWSTR ServiceName OPTIONAL,
|
||||||
|
_Inout_ SC_HANDLE *ServiceHandle);
|
||||||
|
|
||||||
|
DWORD KmtFltCloseService(
|
||||||
|
_Inout_ SC_HANDLE *ServiceHandle);
|
||||||
|
|
||||||
#endif /* !defined _KMTESTS_H_ */
|
#endif /* !defined _KMTESTS_H_ */
|
||||||
|
|
|
@ -12,6 +12,19 @@
|
||||||
|
|
||||||
#define SERVICE_ACCESS (SERVICE_START | SERVICE_STOP | DELETE)
|
#define SERVICE_ACCESS (SERVICE_START | SERVICE_STOP | DELETE)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an internal function not meant for use by the kmtests app,
|
||||||
|
* so we declare it here instead of kmtest.h
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtpCreateService(
|
||||||
|
IN PCWSTR ServiceName,
|
||||||
|
IN PCWSTR ServicePath,
|
||||||
|
IN PCWSTR DisplayName OPTIONAL,
|
||||||
|
IN DWORD ServiceType,
|
||||||
|
OUT SC_HANDLE *ServiceHandle);
|
||||||
|
|
||||||
|
|
||||||
static SC_HANDLE ScmHandle;
|
static SC_HANDLE ScmHandle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,35 +93,11 @@ KmtCreateService(
|
||||||
IN PCWSTR DisplayName OPTIONAL,
|
IN PCWSTR DisplayName OPTIONAL,
|
||||||
OUT SC_HANDLE *ServiceHandle)
|
OUT SC_HANDLE *ServiceHandle)
|
||||||
{
|
{
|
||||||
DWORD Error = ERROR_SUCCESS;
|
return KmtpCreateService(ServiceName,
|
||||||
WCHAR DriverPath[MAX_PATH];
|
ServicePath,
|
||||||
HRESULT result = S_OK;
|
DisplayName,
|
||||||
|
SERVICE_KERNEL_DRIVER,
|
||||||
assert(ServiceHandle);
|
ServiceHandle);
|
||||||
assert(ServiceName && ServicePath);
|
|
||||||
|
|
||||||
if (!GetModuleFileName(NULL, DriverPath, sizeof DriverPath / sizeof DriverPath[0]))
|
|
||||||
error_goto(Error, cleanup);
|
|
||||||
|
|
||||||
assert(wcsrchr(DriverPath, L'\\') != NULL);
|
|
||||||
wcsrchr(DriverPath, L'\\')[1] = L'\0';
|
|
||||||
|
|
||||||
result = StringCbCat(DriverPath, sizeof DriverPath, ServicePath);
|
|
||||||
if (FAILED(result))
|
|
||||||
error_value_goto(Error, result, cleanup);
|
|
||||||
|
|
||||||
if (GetFileAttributes(DriverPath) == INVALID_FILE_ATTRIBUTES)
|
|
||||||
error_goto(Error, cleanup);
|
|
||||||
|
|
||||||
*ServiceHandle = CreateService(ScmHandle, ServiceName, DisplayName,
|
|
||||||
SERVICE_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,
|
|
||||||
SERVICE_ERROR_NORMAL, DriverPath, NULL, NULL, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
if (!*ServiceHandle)
|
|
||||||
error_goto(Error, cleanup);
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
return Error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -307,3 +296,47 @@ DWORD KmtCloseService(
|
||||||
cleanup:
|
cleanup:
|
||||||
return Error;
|
return Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Private function, not meant for use in kmtests
|
||||||
|
* See KmtCreateService & KmtFltCreateService
|
||||||
|
*/
|
||||||
|
DWORD
|
||||||
|
KmtpCreateService(
|
||||||
|
IN PCWSTR ServiceName,
|
||||||
|
IN PCWSTR ServicePath,
|
||||||
|
IN PCWSTR DisplayName OPTIONAL,
|
||||||
|
IN DWORD ServiceType,
|
||||||
|
OUT SC_HANDLE *ServiceHandle)
|
||||||
|
{
|
||||||
|
DWORD Error = ERROR_SUCCESS;
|
||||||
|
WCHAR DriverPath[MAX_PATH];
|
||||||
|
HRESULT result = S_OK;
|
||||||
|
|
||||||
|
assert(ServiceHandle);
|
||||||
|
assert(ServiceName && ServicePath);
|
||||||
|
|
||||||
|
if (!GetModuleFileName(NULL, DriverPath, sizeof DriverPath / sizeof DriverPath[0]))
|
||||||
|
error_goto(Error, cleanup);
|
||||||
|
|
||||||
|
assert(wcsrchr(DriverPath, L'\\') != NULL);
|
||||||
|
wcsrchr(DriverPath, L'\\')[1] = L'\0';
|
||||||
|
|
||||||
|
result = StringCbCat(DriverPath, sizeof DriverPath, ServicePath);
|
||||||
|
if (FAILED(result))
|
||||||
|
error_value_goto(Error, result, cleanup);
|
||||||
|
|
||||||
|
if (GetFileAttributes(DriverPath) == INVALID_FILE_ATTRIBUTES)
|
||||||
|
error_goto(Error, cleanup);
|
||||||
|
|
||||||
|
*ServiceHandle = CreateService(ScmHandle, ServiceName, DisplayName,
|
||||||
|
SERVICE_ACCESS, ServiceType, SERVICE_DEMAND_START,
|
||||||
|
SERVICE_ERROR_NORMAL, DriverPath, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if (!*ServiceHandle)
|
||||||
|
error_goto(Error, cleanup);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
return Error;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue