You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
546 lines
18 KiB
546 lines
18 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
event.c
|
|
|
|
Abstract:
|
|
|
|
This module contains miscellaneous Configuration Manager API routines.
|
|
|
|
CMP_RegisterNotification
|
|
CMP_UnregisterNotification
|
|
|
|
Author:
|
|
|
|
Jim Cavalaris (jamesca) 05-05-2001
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
|
|
Revision History:
|
|
|
|
05-May-2001 jamesca
|
|
|
|
Creation and initial implementation (moved from cfgmgr32\misc.c).
|
|
|
|
--*/
|
|
|
|
|
|
//
|
|
// includes
|
|
//
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "cfgi.h"
|
|
|
|
#include <dbt.h>
|
|
#include <winsvcp.h>
|
|
|
|
|
|
//
|
|
// global data
|
|
//
|
|
#ifndef _WIN64
|
|
extern BOOL IsWow64; // set if we're running under WOW64, externed from setupapi\dll.c
|
|
#endif // _WIN64
|
|
|
|
|
|
//
|
|
// Client-side structure used to store context handles.
|
|
//
|
|
|
|
typedef struct _PNP_CLIENT_CONTEXT {
|
|
ULONG PNP_CC_Signature;
|
|
ULONG_PTR PNP_CC_ContextHandle;
|
|
} PNP_CLIENT_CONTEXT, *PPNP_CLIENT_CONTEXT;
|
|
|
|
|
|
//
|
|
// GetModuleFileNameExW, dynamically loaded by CMP_RegisterNotification
|
|
//
|
|
typedef DWORD (WINAPI *PFN_GETMODULEFILENAMEEXW)(
|
|
IN HANDLE hProcess,
|
|
IN HMODULE hModule,
|
|
OUT LPWSTR lpFilename,
|
|
IN DWORD nSize
|
|
);
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CMP_RegisterNotification(
|
|
IN HANDLE hRecipient,
|
|
IN LPBYTE NotificationFilter,
|
|
IN DWORD Flags,
|
|
OUT PNP_NOTIFICATION_CONTEXT *Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine registers the specified handle for the type of Plug and Play
|
|
device event notification specified by the NotificationFilter.
|
|
|
|
Parameters:
|
|
|
|
hRecipient - Handle to register as the notification recipient. May be a
|
|
window handle or service status handle, and must be specified
|
|
with the appropriate flags.
|
|
|
|
NotificationFilter - Specifies a notification filter that specifies the type
|
|
of events to register for. The Notification filter specifies a
|
|
pointer to a DEV_BROADCAST_HEADER structure, whose
|
|
dbch_devicetype member indicates the actual type of the
|
|
NotificationFilter.
|
|
|
|
Currently, may be one of the following:
|
|
|
|
DEV_BROADCAST_HANDLE (DBT_DEVTYP_HANDLE type)
|
|
|
|
DEV_BROADCAST_DEVICEINTERFACE (DBT_DEVTYP_DEVICEINTERFACE type)
|
|
|
|
Flags - Specifies additional flags for the operation. The following flags
|
|
are currently defined:
|
|
|
|
DEVICE_NOTIFY_WINDOW_HANDLE -
|
|
hRecipient specifies a window handle.
|
|
|
|
DEVICE_NOTIFY_SERVICE_HANDLE -
|
|
hRecipient specifies a service status handle.
|
|
|
|
DEVICE_NOTIFY_COMPLETION_HANDLE -
|
|
Not currently implemented.
|
|
|
|
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES - Specifies that the
|
|
notification request is for all device interface change
|
|
events. Only valid with a DEV_BROADCAST_DEVICEINTERFACE
|
|
NotificationFilter. If this flag is specified the
|
|
dbcc_classguid field is ignored.
|
|
|
|
Context - Receives a notification context. This context is supplied to the
|
|
server via PNP_UnregisterNotification to unregister the
|
|
corresponding notification handle.
|
|
|
|
Return Value:
|
|
|
|
Returns CR_SUCCESS if the component was successfully registered for
|
|
notification. Returns CR_FAILURE otherwise.
|
|
|
|
Notes:
|
|
|
|
This CM API does not allow the client to specify a server name because the
|
|
RPC call is always made to the local server. This routine will never call
|
|
the corresponding RPC server interface (PNP_RegisterNotification)
|
|
remotely. Additionally, this routine is private, and should only be called
|
|
via user32!RegisterDeviceNotification.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
handle_t hBinding = NULL;
|
|
ULONG ulSize;
|
|
PPNP_CLIENT_CONTEXT ClientContext;
|
|
ULONG64 ClientContext64;
|
|
WCHAR ClientName[MAX_SERVICE_NAME_LEN];
|
|
|
|
|
|
try {
|
|
//
|
|
// validate parameters
|
|
//
|
|
if (!ARGUMENT_PRESENT(Context)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
*Context = NULL;
|
|
|
|
if ((!ARGUMENT_PRESENT(NotificationFilter)) ||
|
|
(hRecipient == NULL)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// DEVICE_NOTIFY_BITS is a private mask, defined specifically for
|
|
// validation by the client and server. It contains the bitmask for all
|
|
// handle types (DEVICE_NOTIFY_COMPLETION_HANDLE specifically excluded
|
|
// by the server), and all other flags that are currently defined - both
|
|
// public and reserved.
|
|
//
|
|
if (INVALID_FLAGS(Flags, DEVICE_NOTIFY_BITS)) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Make sure the caller didn't specify any private flags. Flags in this
|
|
// range are currently reserved for use by CFGMGR32 and UMPNPMGR only!!
|
|
//
|
|
if ((Flags & DEVICE_NOTIFY_RESERVED_MASK) != 0) {
|
|
Status = CR_INVALID_FLAG;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// validate the notification filter. UlSize is used as an explicit
|
|
// parameter to let RPC know how much data to marshall, though the
|
|
// server validates the size in the structure against it as well.
|
|
//
|
|
ulSize = ((PDEV_BROADCAST_HDR)NotificationFilter)->dbch_size;
|
|
|
|
if (ulSize < sizeof(DEV_BROADCAST_HDR)) {
|
|
Status = CR_INVALID_DATA;
|
|
goto Clean0;
|
|
}
|
|
|
|
#ifndef _WIN64
|
|
//
|
|
// Determine if the 32 bit client is running on WOW64, and set the
|
|
// reserved flags appropriately.
|
|
//
|
|
if (IsWow64) {
|
|
Flags |= DEVICE_NOTIFY_WOW64_CLIENT;
|
|
}
|
|
#endif // _WIN64
|
|
|
|
//
|
|
// setup rpc binding handle (don't need string table handle)
|
|
// this is always to the local server, by definition
|
|
//
|
|
if (!PnPGetGlobalHandles(NULL, NULL, &hBinding)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Allocate client context handle structure from the local process heap
|
|
// rather than setupapi's own heap so that the returned pointer can
|
|
// transcend the dynamic loading and unloading of SETUPAPI.DLL between
|
|
// calls to CMP_RegisterNotification and CMP_UnregisterNotification.
|
|
// (this is done by USER32.DLL for RegisterDeviceNotification and
|
|
// UnregisterDeviceNotification).
|
|
//
|
|
ClientContext = LocalAlloc(0, sizeof(PNP_CLIENT_CONTEXT));
|
|
|
|
if (ClientContext == NULL) {
|
|
Status = CR_OUT_OF_MEMORY;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Put a signature on the client context, to be checked (and
|
|
// invalidated) at unregistration time.
|
|
//
|
|
ClientContext->PNP_CC_Signature = CLIENT_CONTEXT_SIGNATURE;
|
|
ClientContext->PNP_CC_ContextHandle = 0;
|
|
|
|
ZeroMemory(ClientName, sizeof(ClientName));
|
|
|
|
if ((Flags & DEVICE_NOTIFY_HANDLE_MASK) == DEVICE_NOTIFY_WINDOW_HANDLE) {
|
|
|
|
DWORD dwLength = 0;
|
|
|
|
//
|
|
// first, try to retrieve the window text of the window being
|
|
// registered for device event notification. we'll pass this into
|
|
// UMPNPMGR for use as an identifier when the window vetoes device
|
|
// event notifications.
|
|
//
|
|
dwLength = GetWindowText(hRecipient,
|
|
ClientName,
|
|
MAX_SERVICE_NAME_LEN);
|
|
if (dwLength == 0) {
|
|
//
|
|
// GetWindowText did not return any text. Attempt to retrieve
|
|
// the process module name instead.
|
|
//
|
|
DWORD dwProcessId;
|
|
HANDLE hProcess;
|
|
HMODULE hPsApiDll;
|
|
PFN_GETMODULEFILENAMEEXW pfnGetModuleFileNameExW;
|
|
|
|
//
|
|
// get the id of the process that this window handle is
|
|
// associated with.
|
|
//
|
|
|
|
if (GetWindowThreadProcessId(hRecipient, &dwProcessId)) {
|
|
|
|
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
|
|
FALSE,
|
|
dwProcessId);
|
|
|
|
if (hProcess) {
|
|
|
|
//
|
|
// load the psapi.dll library and find the the
|
|
// GetModuleFileNameExW entry point.
|
|
//
|
|
|
|
hPsApiDll = LoadLibrary(L"psapi.dll");
|
|
|
|
if (hPsApiDll) {
|
|
|
|
pfnGetModuleFileNameExW =
|
|
(PFN_GETMODULEFILENAMEEXW)GetProcAddress(hPsApiDll,
|
|
"GetModuleFileNameExW");
|
|
|
|
if (pfnGetModuleFileNameExW) {
|
|
//
|
|
// retrieve the module file name for the process
|
|
// this window handle is associated with.
|
|
//
|
|
dwLength = pfnGetModuleFileNameExW(hProcess,
|
|
NULL,
|
|
ClientName,
|
|
MAX_SERVICE_NAME_LEN);
|
|
} else {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_ERRORS | DBGF_EVENT,
|
|
"CFGMGR32: CMP_RegisterNotification: GetProcAddress returned error = %d\n",
|
|
GetLastError()));
|
|
}
|
|
|
|
FreeLibrary(hPsApiDll);
|
|
}
|
|
CloseHandle(hProcess);
|
|
} else {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_ERRORS | DBGF_EVENT,
|
|
"CFGMGR32: CMP_RegisterNotification: OpenProcess returned error = %d\n",
|
|
GetLastError()));
|
|
}
|
|
|
|
} else {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_ERRORS | DBGF_EVENT,
|
|
"CFGMGR32: CMP_RegisterNotification: GetWindowThreadProcessId returned error = %d\n",
|
|
GetLastError()));
|
|
}
|
|
}
|
|
|
|
if (dwLength == 0) {
|
|
//
|
|
// could not retrieve any identifier for this window.
|
|
//
|
|
ClientName[0] = UNICODE_NULL;
|
|
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_WARNINGS | DBGF_EVENT,
|
|
"CFGMGR32: CMP_RegisterNotification: Could not retieve any name for window %d!!\n",
|
|
hRecipient));
|
|
}
|
|
|
|
} else if ((Flags & DEVICE_NOTIFY_HANDLE_MASK) == DEVICE_NOTIFY_SERVICE_HANDLE) {
|
|
|
|
//
|
|
// Get the name of the service corresponding to the service status
|
|
// handle supplied.
|
|
//
|
|
if (NO_ERROR != I_ScPnPGetServiceName(hRecipient, ClientName, MAX_SERVICE_NAME_LEN)) {
|
|
Status = CR_INVALID_DATA;
|
|
LocalFree(ClientContext);
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// Just set this to point to the buffer we use. PNP_RegisterNotification will unpack it.
|
|
//
|
|
hRecipient = ClientName;
|
|
}
|
|
|
|
//
|
|
// The client context pointer is now always transmitted to the server as
|
|
// a 64-bit value - which is large enough to hold the pointer in both
|
|
// the 32-bit and 64-bit cases. This standardizes the RPC interface for
|
|
// all clients, since RPC will always marshall a 64-bit value. The
|
|
// server will also store the value internally as a 64-bit value, but
|
|
// cast it to an HDEVNOTIFY of appropriate size for the client.
|
|
//
|
|
// Note that we have RPC transmit this parameter simply as a pointer to
|
|
// a ULONG64 (which is actually a pointer itself). We don't transmit it
|
|
// as a pointer to a PPNP_CLIENT_CONTEXT (which is also a pointer)
|
|
// because RPC would instead allocate the memory to marshall the
|
|
// contents of the structure to the server. The server would get a
|
|
// pointer to RPC allocated memory, not the actual value of the client
|
|
// pointer - which is all we really want to send in the first place.
|
|
// The server does not actually use this value as a pointer to anything.
|
|
//
|
|
ClientContext64 = (ULONG64)ClientContext;
|
|
|
|
//
|
|
// No special privileges are required by the server
|
|
//
|
|
|
|
RpcTryExcept {
|
|
//
|
|
// call rpc service entry point
|
|
//
|
|
Status = PNP_RegisterNotification(
|
|
hBinding,
|
|
(ULONG_PTR)hRecipient,
|
|
ClientName,
|
|
NotificationFilter,
|
|
ulSize,
|
|
Flags,
|
|
&((PNP_NOTIFICATION_CONTEXT)(ClientContext->PNP_CC_ContextHandle)),
|
|
GetCurrentProcessId(),
|
|
&((ULONG64)ClientContext64));
|
|
|
|
} RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_WARNINGS | DBGF_EVENT,
|
|
"PNP_RegisterNotification caused an exception (%d)\n",
|
|
RpcExceptionCode()));
|
|
|
|
Status = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
|
|
if (Status != CR_SUCCESS) {
|
|
//
|
|
// Something went wrong. If we built a context handle
|
|
// let it dangle; we can't tell RPC it's gone. (will get rundown)
|
|
// If it's NULL, free the memory.
|
|
// Don't tell the client we succeeded
|
|
//
|
|
if (ClientContext->PNP_CC_ContextHandle == 0) {
|
|
LocalFree (ClientContext);
|
|
}
|
|
*Context = NULL;
|
|
} else {
|
|
*Context = (PNP_NOTIFICATION_CONTEXT)ClientContext;
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CMP_RegisterNotification
|
|
|
|
|
|
|
|
CONFIGRET
|
|
CMP_UnregisterNotification(
|
|
IN ULONG_PTR Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine unregisters the Plug and Play device event notification entry
|
|
represented by the specified notification context.
|
|
|
|
Parameters:
|
|
|
|
Context - Supplies a client notification context.
|
|
|
|
Return Value:
|
|
|
|
Returns CR_SUCCESS if the component was successfully unregistered for
|
|
notification. If the function fails, the return value is one of the
|
|
following:
|
|
|
|
CR_FAILURE,
|
|
CR_INVALID_POINTER
|
|
|
|
Notes:
|
|
|
|
This CM API does not allow the client to specify a server name because the
|
|
RPC call is always made to the local server. This routine will never call
|
|
the corresponding RPC server interface (PNP_UnregisterNotification)
|
|
remotely. Additionally, this routine is private, and should only be called
|
|
via user32!UnregisterDeviceNotification.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONFIGRET Status = CR_SUCCESS;
|
|
handle_t hBinding = NULL;
|
|
PPNP_CLIENT_CONTEXT ClientContext = (PPNP_CLIENT_CONTEXT)Context;
|
|
|
|
try {
|
|
//
|
|
// validate parameters
|
|
//
|
|
if (Context == 0 || Context == (ULONG_PTR)(-1)) {
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// make sure the client context signature is valid
|
|
//
|
|
if (ClientContext->PNP_CC_Signature != CLIENT_CONTEXT_SIGNATURE) {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_ERRORS,
|
|
"CMP_UnregisterNotification: bad signature on client handle\n"));
|
|
Status = CR_INVALID_POINTER;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// setup rpc binding handle (don't need string table handle)
|
|
// this is always to the local server, by definition
|
|
//
|
|
if (!PnPGetGlobalHandles(NULL, NULL, &hBinding)) {
|
|
Status = CR_FAILURE;
|
|
goto Clean0;
|
|
}
|
|
|
|
//
|
|
// No special privileges are required by the server
|
|
//
|
|
|
|
RpcTryExcept {
|
|
//
|
|
// call rpc service entry point
|
|
//
|
|
Status = PNP_UnregisterNotification(
|
|
hBinding,
|
|
(PPNP_NOTIFICATION_CONTEXT)&(ClientContext->PNP_CC_ContextHandle));
|
|
|
|
} RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) {
|
|
KdPrintEx((DPFLTR_PNPMGR_ID,
|
|
DBGF_WARNINGS | DBGF_EVENT,
|
|
"PNP_UnregisterNotification caused an exception (%d)\n",
|
|
RpcExceptionCode()));
|
|
|
|
Status = MapRpcExceptionToCR(RpcExceptionCode());
|
|
}
|
|
RpcEndExcept
|
|
|
|
if (Status == CR_SUCCESS) {
|
|
//
|
|
// invalidate the client context signature and free the client
|
|
// context structure.
|
|
//
|
|
ClientContext->PNP_CC_Signature = 0;
|
|
LocalFree((PVOID)Context);
|
|
}
|
|
|
|
Clean0:
|
|
NOTHING;
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = CR_FAILURE;
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // CMP_UnregisterNotification
|
|
|
|
|