Leaked source code of windows server 2003
 
 
 
 
 
 

2175 lines
50 KiB

/*++
Copyright (C) Microsoft Corporation, 1996 - 1999
Module Name:
wndproc.CPP
Abstract:
This is the window procedure for STI server process
Author:
Vlad Sadovsky (vlads) 12-20-96
Revision History:
20-Dec-1996 Vlads Created
28-Sep-1997 VladS Added code for SCM glue
20-May-2000 ByronC Replaced windows messaging
--*/
//
// Headers
//
#include "precomp.h"
#include "stiexe.h"
#include <windowsx.h>
#include "device.h"
#include "monui.h"
#include <validate.h>
#include <apiutil.h>
#include <shellapi.h>
#include <devguid.h>
#include "wiamindr.h"
//
// Definitions
//
#define REFRESH_ASYNC 1 // Do refresh asyncronously
#define USE_WORKER_THREAD 1 // Run configuration changes on separate worker thread
#define USE_BROADCASTSYSTEM 1 // Rebroadcast device arrivals/removal
#define DEVICE_REFRESH_WAIT_TIME 30000 // Wait time in milliseconds
//
// Interval to delay refreshing device list after add new device notification received
//
#define REFRESH_DELAY 3000
#define BOOT_REFRESH_DELAY 5000
#define STI_MSG_WAIT_TIME 1
//
// External references
//
extern BOOL g_fUIPermitted;
extern DWORD g_dwCurrentState;
extern LONG g_lTotalActiveDevices;
extern LONG g_lGlobalDeviceId;
extern UINT g_uiDefaultPollTimeout;
extern HDEVNOTIFY g_hStiServiceNotificationSink ;
extern HWND g_hStiServiceWindow;
extern BOOL g_fUseServiceCtrlSink;
extern BOOL g_fFirstDevNodeChangeMsg;
//
// Global Data
//
//
// Static data
//
//
// Prototypes
//
LRESULT CALLBACK
StiExeWinProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
DWORD
StiWnd_OnServiceControlMessage(
HWND hwnd,
WPARAM wParam,
LPARAM lParam
);
BOOL
OnSetParameters(
WPARAM wParam,
LPARAM lParam
);
BOOL
OnDoRefreshActiveDeviceList(
WPARAM wParam,
LPARAM lParam
);
BOOL
OnAddNewDevice(
DEVICE_BROADCAST_INFO *psDevBroadcast
);
BOOL
OnRemoveActiveDevice(
DEVICE_BROADCAST_INFO *psDevBroadcast,
BOOL fRebroadcastRemoval
);
VOID
ConfigChangeThread(
LPVOID lpParameter
);
VOID
DebugDumpDeviceList(
VOID
);
VOID
DebugPurgeDeviceList(
VOID *pContext
);
VOID
RefreshDeviceCallback(
VOID * pContext
);
DWORD
ResetSavedWindowPos(
HWND hWnd
);
DWORD
SaveWindowPos(
HWND hWnd
);
VOID
DumpDeviceChangeData(
HWND hWnd,
WPARAM wParam,
LPARAM lParam
);
VOID
StiServiceStop(
VOID
);
VOID
StiServicePause(
VOID
);
VOID
StiServiceResume(
VOID
);
VOID
BroadcastSTIChange(
DEVICE_BROADCAST_INFO *psDevBroadcastInfo,
LPTSTR lpszStiAction
);
VOID
BroadcastSysMessageThreadProc(
VOID *pContext
);
//
// Message handlers prototypes
//
BOOL StiWnd_OnQueryEndSession(HWND hwnd);
VOID StiWnd_OnEndSession(HWND hwnd, BOOL fEnding);
int StiWnd_OnCreate(HWND hwnd,LPCREATESTRUCT lpCreateStruct);
VOID StiWnd_OnDoRefreshActiveDeviceList(WPARAM wParam,LPARAM lParam);
BOOL StiWnd_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
VOID StiWnd_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
VOID StiWnd_OnSize(HWND hwnd, UINT state, int cx, int cy);
VOID StiWnd_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos);
VOID StiWnd_OnVScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos);
VOID StiWnd_OnDestroy(HWND hwnd);
VOID StiWnd_OnMenuRefresh(VOID);
VOID StiWnd_OnMenuDeviceList(VOID);
VOID StiWnd_OnMenuSetTimeout(VOID);
VOID StiWnd_OnMenuRemoveAll(VOID);
//
// Utilities
//
BOOL
ParseGUID(
LPGUID pguid,
LPCTSTR ptsz
);
BOOL
IsStillImagePnPMessage(
PDEV_BROADCAST_HDR pDev
);
BOOL
GetDeviceNameFromDevNode(
DEVNODE dnDevNode,
StiCString& strDeviceName
);
//
// Code
//
VOID
WINAPI
StiMessageCallback(
VOID *pArg
)
/*++
Routine Description:
This routine simply calls the Sti message dispatcher (aka winproc). It
is used in conjunction with StiPostMessage to replace ::PostMessage.
Arguments:
pArg - Must be of type STI_MESSAGE
Return Value:
None.
--*/
{
STI_MESSAGE *pMessage = (STI_MESSAGE*)pArg;
LRESULT lRes = 0;
//
// Validate params
//
if (!pMessage) {
DBG_WRN(("::StiMessageCallback, NULL message"));
return;
}
if (IsBadReadPtr(pMessage, sizeof(STI_MESSAGE))) {
DBG_WRN(("::StiMessageCallback, Bad message"));
return;
}
//
// Call StiSvcWinProc to process the message
//
_try {
lRes = StiSvcWinProc(NULL,
pMessage->m_uMsg,
pMessage->m_wParam,
pMessage->m_lParam);
}
_finally {
pMessage = NULL;
}
return;
}
VOID
WINAPI
StiRefreshCallback(
VOID *pArg
)
/*++
Routine Description:
This routine simply calls RefreshDeviceList.
Arguments:
pArg - Must be of type STI_MESSAGE
Return Value:
None.
--*/
{
STI_MESSAGE *pMessage = (STI_MESSAGE*)pArg;
LRESULT lRes = 0;
//
// Validate params
//
if (!pMessage) {
DBG_WRN(("::StiRefreshCallback, NULL message"));
return;
}
if (IsBadReadPtr(pMessage, sizeof(STI_MESSAGE))) {
DBG_WRN(("::StiRefreshCallback, Bad message"));
return;
}
//
// Call RefreshDeviceList
//
_try {
RefreshDeviceList((WORD)pMessage->m_wParam,
(WORD)pMessage->m_lParam);
}
_finally {
pMessage = NULL;
}
return;
}
LRESULT
StiSendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
)
/*++
Routine Description:
This routine replaces the normal windows messaging SendMessage by calling
the message dispatcher (StiSvcWinProc) directly. It replaces ::SendMessage.
Arguments:
hWnd - handle to destination window. This is not used.
Msg - message
wParam - first message parameter
lParam - second message parameterReturn Value:
Return Value:
--*/
{
return StiSvcWinProc(NULL, Msg, wParam, lParam);
}
BOOL
StiPostMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
)
/*++
Routine Description:
This routine simulates PostMessage by putting StiMessageCallback on the
Scheduler's queue.
Arguments:
hWnd - handle to destination window. This is not used.
Msg - message
wParam - first message parameter
lParam - second message parameterReturn Value:
Return Value:
TRUE - success
FALSE - message could not be posted
--*/
{
BOOL bRet = FALSE;
STI_MESSAGE *pMsg = new STI_MESSAGE(Msg, wParam, lParam);
if (pMsg) {
if (ScheduleWorkItem((PFN_SCHED_CALLBACK) StiMessageCallback,
pMsg,
STI_MSG_WAIT_TIME,
NULL)) {
bRet = TRUE;
} else {
delete pMsg;
}
}
if (!bRet) {
DBG_WRN(("::StiPostMessage, could not post message"));
}
return bRet;
}
BOOL
StiRefreshWithDelay(
ULONG ulDelay,
WPARAM wParam,
LPARAM lParam
)
/*++
Routine Description:
This routine simulates PostMessage by putting StiMessageCallback on the
Scheduler's queue with a delay of ulDelay.
Arguments:
ulDelay - delay in milliseconds
Msg - message
wParam - first message parameter
lParam - second message parameter
Return Value:
TRUE - success
FALSE - message could not be posted
--*/
{
BOOL bRet = FALSE;
STI_MESSAGE *pMsg = new STI_MESSAGE(0, wParam, lParam);
if (pMsg) {
if (ScheduleWorkItem((PFN_SCHED_CALLBACK) StiRefreshCallback,
pMsg,
ulDelay,
NULL)) {
bRet = TRUE;
} else {
delete pMsg;
}
}
if (!bRet) {
DBG_WRN(("::StiRefreshWithDelay, could not post message"));
}
return bRet;
}
HWND
WINAPI
CreateMasterWindow(
VOID
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
#ifndef WINNT
//Don't use windows messaging on NT
DBG_FN(CreateMasterWindow);
WNDCLASSEX wc;
HWND hwnd = FindWindow(g_szClass,NULL);
if (hwnd) {
//
// Notify master window that we started.
//
if (g_fUIPermitted) {
::ShowWindow(hwnd,g_fUIPermitted ? SW_SHOWNORMAL : SW_HIDE);
}
return NULL;
}
//
// Create class
//
memset(&wc,0,sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = StiExeWinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hInst;
wc.hIcon = LoadIcon(NULL,MAKEINTRESOURCE(1));
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
wc.lpszClassName = g_szClass;
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
if (!RegisterClassEx(&wc))
return NULL;
#ifndef WINNT
#ifdef FE_IME
// Disable IME processing on Millenium
ImmDisableIME(::GetCurrentThreadId());
#endif
#endif
g_hMainWindow = CreateWindowEx(0, // Style bits
g_szClass, // Class name
g_szTitle, // Title
WS_OVERLAPPEDWINDOW , // Window style bits
CW_USEDEFAULT, // x
CW_USEDEFAULT, // y
CW_USEDEFAULT, // h
CW_USEDEFAULT, // w
NULL, // Parent
NULL, // Menu
g_hInst, // Module instance
NULL); // Options
if(g_hMainWindow) {
// Register custom message
g_StiFileLog->SetLogWindowHandle(g_hMainWindow);
}
DBG_ERR(("WIASERVC :: CreateMasterWindow. Handle = %X ",g_hMainWindow);
#endif
return g_hMainWindow;
}
LRESULT
CALLBACK
StiExeWinProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
/*++
Routine Description:
Master window callback procedure
Arguments:
Return Value:
None.
--*/
{
switch(uMsg) {
case WM_COMMAND:
HANDLE_WM_COMMAND(hwnd, wParam, lParam, StiWnd_OnCommand);
break;
case WM_CREATE:
return HANDLE_WM_CREATE(hwnd, wParam, lParam, StiWnd_OnCreate);
break;
case STIMON_MSG_SET_PARAMETERS:
OnSetParameters(wParam,lParam);
break;
case STIMON_MSG_VISUALIZE:
{
//
// Make ourselves visible or hidden
//
BOOL fShow = (BOOL)wParam;
g_fUIPermitted = fShow;
ShowWindow(hwnd,fShow ? SW_SHOWNORMAL : SW_HIDE);
g_StiFileLog->SetReportMode(g_StiFileLog->QueryReportMode() | STI_TRACE_LOG_TOUI);
}
break;
case WM_DESTROY:
HANDLE_WM_DESTROY(hwnd, wParam, lParam, StiWnd_OnDestroy);
break;
case WM_ENDSESSION:
return HANDLE_WM_ENDSESSION(hwnd, wParam, lParam, StiWnd_OnEndSession);
case WM_QUERYENDSESSION:
return HANDLE_WM_QUERYENDSESSION(hwnd, wParam, lParam, StiWnd_OnQueryEndSession);
case WM_CLOSE:
DBG_TRC(("Service instance received WM_CLOSE"));
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0L;
} /* endproc WinProc */
BOOL
WINAPI
OnSetParameters(
WPARAM wParam,
LPARAM lParam
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
switch(wParam) {
case STIMON_MSG_SET_TIMEOUT:
return lParam ? ResetAllPollIntervals((UINT)lParam) : 0;
default:
;
}
return 0;
}
BOOL
WINAPI
StiWnd_OnQueryEndSession(
HWND hwnd
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
return TRUE;
}
VOID
WINAPI
StiWnd_OnEndSession(
HWND hwnd,
BOOL fEnding
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
return;
}
BOOL
WINAPI
StiWnd_OnCreate(
HWND hwnd,
LPCREATESTRUCT lpCreateStruct
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
// Restore window charateristics
ResetSavedWindowPos(hwnd);
return TRUE;
}
VOID
WINAPI
StiWnd_OnCommand(
HWND hwnd,
int id,
HWND hwndCtl,
UINT codeNotify
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
switch (id) {
case IDM_TOOLS_DEVLIST:
StiWnd_OnMenuDeviceList();
break;
case IDM_TOOLS_REFRESH:
StiWnd_OnMenuRefresh();
break;
case IDM_TOOLS_TIMEOUT:
StiWnd_OnMenuSetTimeout();
break;
case IDM_TOOLS_REMOVEALL:
StiWnd_OnMenuRemoveAll();
break;
}
}
VOID
WINAPI
StiWnd_OnDestroy(
HWND hwnd
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
DBG_TRC(("Service instance received WM_DESTROY"));
// Save current window position
SaveWindowPos(hwnd);
// Main window is going away.
PostQuitMessage(0);
return;
}
//
// Menu verb handlers
//
VOID
WINAPI
StiWnd_OnMenuRefresh(
VOID
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
STIMONWPRINTF(TEXT("Menu: Refreshing device list "));
OnDoRefreshActiveDeviceList(STIMON_MSG_REFRESH_REREAD,
STIMON_MSG_REFRESH_NEW | STIMON_MSG_REFRESH_EXISTING
);
STIMONWPRINTF(TEXT("Done refreshing device list. Active device count:%d Current device id:%d "),
g_lTotalActiveDevices,g_lGlobalDeviceId);
}
VOID
WINAPI
StiWnd_OnMenuDeviceList(
VOID
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
STIMONWPRINTF(TEXT("Menu: Displaying device list "));
DebugDumpDeviceList();
STIMONWPRINTF(TEXT("Active device count:%d Current device id:%d "),
g_lTotalActiveDevices,g_lGlobalDeviceId);
}
VOID
StiWnd_OnMenuRemoveAll(
VOID
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
STIMONWPRINTF(TEXT("Menu: removing all devices "));
#ifdef USE_WORKER_THREAD
HANDLE hThread;
DWORD dwThread;
hThread = ::CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)DebugPurgeDeviceList,
(LPVOID)0,
0,
&dwThread);
if ( hThread )
CloseHandle(hThread);
#else
//
// Try to schedule refresh work item
//
DWORD dwSchedulerCookie = ::ScheduleWorkItem(
(PFN_SCHED_CALLBACK) DebugPurgeDeviceList,
(LPVOID)0,
10);
if ( !dwSchedulerCookie ){
ASSERT(("Refresh routine could not schedule work item", 0));
STIMONWPRINTF(TEXT("Could not schedule asyncronous removal"));
}
#endif
}
VOID
WINAPI
StiWnd_OnMenuSetTimeout(
VOID
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
CSetTimeout cdlg(IDD_SETTIMEOUT,::GetActiveWindow(),NULL,g_uiDefaultPollTimeout);
if ( (cdlg.CreateModal() == IDOK) && (cdlg.m_fValidChange) ) {
g_uiDefaultPollTimeout = cdlg.GetNewTimeout();
if (cdlg.IsAllChange()) {
// Update all devices
ResetAllPollIntervals(g_uiDefaultPollTimeout);
}
}
}
DWORD
WINAPI
ResetSavedWindowPos(
HWND hwnd
)
/*++
Loads the window position structure from registry and resets
Returns:
Win32 error code. NO_ERROR on success
--*/
{
DWORD dwError = NO_ERROR;
BUFFER buf;
RegEntry re(REGSTR_PATH_STICONTROL,HKEY_LOCAL_MACHINE);
re.GetValue(REGSTR_VAL_DEBUG_STIMONUIWIN,&buf);
if (buf.QuerySize() >= sizeof(WINDOWPLACEMENT) ) {
WINDOWPLACEMENT *pWinPos = (WINDOWPLACEMENT *)buf.QueryPtr();
//
// Command line and registry settings override last saved parameters
//
pWinPos->showCmd = g_fUIPermitted ? SW_SHOWNORMAL : SW_HIDE;
dwError = ::SetWindowPlacement(hwnd,(WINDOWPLACEMENT *)buf.QueryPtr());
}
else {
::ShowWindow(hwnd,g_fUIPermitted ? SW_SHOWNORMAL : SW_HIDE);
}
return dwError;
} //
DWORD
WINAPI
SaveWindowPos(
HWND hwnd
)
/*++
Loads the window position structure from registry and resets
Returns:
Win32 error code. NO_ERROR on success
--*/
{
DWORD dwError = NO_ERROR;
BUFFER buf(sizeof(WINDOWPLACEMENT));
if (buf.QuerySize() >= sizeof(WINDOWPLACEMENT) ) {
WINDOWPLACEMENT *pWinPos = (WINDOWPLACEMENT *)buf.QueryPtr();
pWinPos->length = sizeof(WINDOWPLACEMENT);
dwError = ::GetWindowPlacement(hwnd,(WINDOWPLACEMENT *)buf.QueryPtr());
RegEntry re(REGSTR_PATH_STICONTROL,HKEY_LOCAL_MACHINE);
dwError = re.SetValue(REGSTR_VAL_DEBUG_STIMONUIWIN,(unsigned char *)buf.QueryPtr(),buf.QuerySize());
}
else {
dwError = ::GetLastError();
}
return dwError;
} //
//
// Window procedure and handlers for service hidden window
//
LRESULT
WINAPI
CALLBACK
StiSvcWinProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
/*++
Routine Description:
STI service hidden window. Used for queuing actions and receiving
PnP notifications and Power broadcasts
Arguments:
Return Value:
None.
--*/
{
DBG_FN(StiSvcWinProc);
PDEVICE_BROADCAST_INFO pBufDevice;
LRESULT lRet = NOERROR;
DBG_TRC(("Came to Service Window proc .uMsg=%X wParam=%X lParam=%X",uMsg,wParam,lParam));
//
// Give WIA a chance at processing messages. Note that
// WIA hooks both message dispatch and the window proc. so that
// both posted and sent messages can be detected.
//
if (ProcessWiaMsg(hwnd, uMsg, wParam, lParam) == S_OK) {
return 0;
}
switch(uMsg) {
case WM_CREATE:
{
#ifdef WINNT
/*
//*
//* REMOVE all instances where we register for PnP events using window Handle on NT
//*
if (!g_fUseServiceCtrlSink || !g_hStiServiceNotificationSink) {
DEV_BROADCAST_HDR *psh;
DEV_BROADCAST_DEVICEINTERFACE sNotificationFilter;
DWORD dwError;
//
// Register to receive device notifications from PnP
//
psh = (DEV_BROADCAST_HDR *)&sNotificationFilter;
psh->dbch_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
psh->dbch_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
psh->dbch_reserved = 0;
sNotificationFilter.dbcc_classguid = *g_pguidDeviceNotificationsGuid;
CopyMemory(&sNotificationFilter.dbcc_classguid,g_pguidDeviceNotificationsGuid,sizeof(GUID));
sNotificationFilter.dbcc_name[0] = 0x0;
DPRINTF(DM_TRACE, TEXT("Attempting to register with PnP"));
g_hStiServiceNotificationSink =
RegisterDeviceNotification(hwnd,
(LPVOID)&sNotificationFilter,
DEVICE_NOTIFY_WINDOW_HANDLE);
dwError = GetLastError();
if( !g_hStiServiceNotificationSink && (NOERROR != dwError)) {
//
// Failed to create notification sink with PnP subsystem
//
// ASSERT
StiLogTrace(STI_TRACE_ERROR,TEXT("Failed to register with PnP ErrorCode =%d"),dwError);
}
DPRINTF(DM_WARNING ,TEXT("Done register with PnP"));
}
*/
#endif
}
break;
case STIMON_MSG_REFRESH:
OnDoRefreshActiveDeviceList(wParam,lParam);
break;
case STIMON_MSG_REMOVE_DEVICE:
pBufDevice = (PDEVICE_BROADCAST_INFO )lParam;
//
// wParam value indicates whether device removal should be rebroadcasted
//
if (pBufDevice) {
lRet = OnRemoveActiveDevice(pBufDevice,(BOOL)wParam) ? NOERROR : (LRESULT) ::GetLastError();
delete pBufDevice;
}
break;
case STIMON_MSG_ADD_DEVICE:
pBufDevice = (PDEVICE_BROADCAST_INFO )lParam;
//
// New device arrived
//
if (pBufDevice) {
lRet = OnAddNewDevice(pBufDevice)? NOERROR : (LRESULT) ::GetLastError();
delete pBufDevice;
}
break;
case WM_DEVICECHANGE:
DumpDeviceChangeData(hwnd,wParam,lParam);
return (StiWnd_OnDeviceChangeMessage(hwnd,(UINT)wParam,lParam));
break;
case WM_POWERBROADCAST:
return (StiWnd_OnPowerControlMessage(hwnd,(DWORD)wParam,lParam));
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return lRet;
} // endproc WinProc
DWORD
WINAPI
StiWnd_OnPowerControlMessage(
HWND hwnd,
DWORD dwPowerEvent,
LPARAM lParam
)
/*++
Routine Description:
Process power management broadcast messages .
Arguments:
None.
Return Value:
None.
--*/
{
DBG_FN(StiWnd_OnPowerControlMessage);
DWORD dwRet = NO_ERROR;
UINT uiTraceMessage = 0;
#ifdef DEBUG
static LPCTSTR pszPwrEventNames[] = {
TEXT("PBT_APMQUERYSUSPEND"), // 0x0000
TEXT("PBT_APMQUERYSTANDBY"), // 0x0001
TEXT("PBT_APMQUERYSUSPENDFAILED"), // 0x0002
TEXT("PBT_APMQUERYSTANDBYFAILED"), // 0x0003
TEXT("PBT_APMSUSPEND"), // 0x0004
TEXT("PBT_APMSTANDBY"), // 0x0005
TEXT("PBT_APMRESUMECRITICAL"), // 0x0006
TEXT("PBT_APMRESUMESUSPEND"), // 0x0007
TEXT("PBT_APMRESUMESTANDBY"), // 0x0008
// TEXT(" PBTF_APMRESUMEFROMFAILURE"), // 0x00000001
TEXT("PBT_APMBATTERYLOW"), // 0x0009
TEXT("PBT_APMPOWERSTATUSCHANGE"), // 0x000A
TEXT("PBT_APMOEMEVENT"), // 0x000B
TEXT("PBT_UNKNOWN"), // 0x000C
TEXT("PBT_UNKNOWN"), // 0x000D
TEXT("PBT_UNKNOWN"), // 0x000E
TEXT("PBT_UNKNOWN"), // 0x000F
TEXT("PBT_UNKNOWN"), // 0x0010
TEXT("PBT_UNKNOWN"), // 0x0011
TEXT("PBT_APMRESUMEAUTOMATIC"), // 0x0012
};
// UINT uiMsgIndex;
//
// uiMsgIndex = (dwPowerEvent < (sizeof(pszPwrEventNames) / sizeof(CHAR *) )) ?
// (UINT) dwPowerEvent : 0x0010;
DBG_TRC(("Still image APM Broadcast Message:%S Code:%x ",
pszPwrEventNames[dwPowerEvent],dwPowerEvent));
#endif
switch(dwPowerEvent)
{
case PBT_APMQUERYSUSPEND:
//
// Request for permission to suspend
//
if(g_NumberOfActiveTransfers > 0) {
//
// Veto suspend while any transfers are in progress
//
return BROADCAST_QUERY_DENY;
}
SchedulerSetPauseState(TRUE);
dwRet = TRUE;
break;
case PBT_APMQUERYSUSPENDFAILED:
//
// Suspension request denied - do nothing
//
SchedulerSetPauseState(FALSE);
break;
case PBT_APMSUSPEND:
StiServicePause();
uiTraceMessage = MSG_TRACE_PWR_SUSPEND;
break;
case PBT_APMRESUMECRITICAL:
// Operation resuming after critical suspension
// Fall through
case PBT_APMRESUMESUSPEND:
//
// Operation resuming after suspension
// Restart all services which were active at the moment of suspend
//
StiServiceResume();
uiTraceMessage = MSG_TRACE_PWR_RESUME;
g_fFirstDevNodeChangeMsg = TRUE;
break;
default:
dwRet = ERROR_INVALID_PARAMETER;
}
return (dwRet == NOERROR) ? TRUE : FALSE;
}
LRESULT
WINAPI
StiWnd_OnDeviceChangeMessage(
HWND hwnd,
UINT DeviceEvent,
LPARAM lParam
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
DBG_FN(StiWnd_OnDeviceChangeMessage);
//DWORD dwRet = NO_ERROR;
LRESULT lRet = NOERROR;
PDEV_BROADCAST_HDR pDev = (PDEV_BROADCAST_HDR)lParam;
DEVICE_BROADCAST_INFO *pBufDevice;
static LPCTSTR pszDBTEventNames[] = {
TEXT("DBT_DEVICEARRIVAL"), // 0x8000
TEXT("DBT_DEVICEQUERYREMOVE"), // 0x8001
TEXT("DBT_DEVICEQUERYREMOVEFAILED"), // 0x8002
TEXT("DBT_DEVICEREMOVEPENDING"), // 0x8003
TEXT("DBT_DEVICEREMOVECOMPLETE"), // 0x8004
TEXT("DBT_DEVICETYPESPECIFIC"), // 0x8005
};
BOOL fLocatedDeviceInstance = FALSE;
BOOL fNeedReenumerateDeviceList = FALSE;
//
// If the DeviceEvent is DBT_DEVNODES_CHANGED, then lParam will be NULL,
// so skip devnode processing.
//
if ((DeviceEvent != DBT_DEVNODES_CHANGED) &&
(DeviceEvent != DBT_DEVICEARRIVAL)) {
//
// Determine if message is for us
//
if (IsBadReadPtr(pDev,sizeof(PDEV_BROADCAST_HDR))) {
return FALSE;
}
//
// Trace that we are here. For all messages, not intended for StillImage devices , we should refresh
// device list if we are running on WIndows NT and registered for device interfaces other than StillImage
//
PDEV_BROADCAST_DEVNODE pDevNode = (PDEV_BROADCAST_DEVNODE)lParam;
PDEV_BROADCAST_DEVICEINTERFACE pDevInterface = (PDEV_BROADCAST_DEVICEINTERFACE)pDev;
if (IsStillImagePnPMessage(pDev)) {
DBG_TRC(("Still image Device/DevNode PnP Message:%S Type:%x DevNode:%x ",
((DeviceEvent>=0x8000) && (DeviceEvent<=0x8005) ?
pszDBTEventNames[DeviceEvent-0x8000] : TEXT("Unknown DBT message"),
pDev->dbch_devicetype,
pDevNode->dbcd_devnode)));
//
// Update device info set if necessary
//
if (g_pDeviceInfoSet) {
if (DeviceEvent == DBT_DEVICEARRIVAL) {
g_pDeviceInfoSet->ProcessNewDeviceChangeMessage(lParam);
}
}
//
// Get device name and store along with the broadacast structure
//
pBufDevice = new DEVICE_BROADCAST_INFO;
if (!pBufDevice) {
::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
//
// Fill in information we have
//
pBufDevice->m_uiDeviceChangeMessage = DeviceEvent;
pBufDevice->m_strBroadcastedName.CopyString(pDevInterface->dbcc_name) ;
pBufDevice->m_dwDevNode = pDevNode->dbcd_devnode;
fLocatedDeviceInstance = FALSE;
fLocatedDeviceInstance = GetDeviceNameFromDevBroadcast((DEV_BROADCAST_HEADER *)pDev,pBufDevice);
if (fLocatedDeviceInstance) {
DBG_TRC(("DEVICECHANGE: Device (%S) ",(LPCTSTR)pBufDevice->m_strDeviceName));
}
else {
DBG_TRC(("DEVICECHANGE: Device - failed to get device name from broadcast"));
}
//
// We don't need broadcast information if device instance had not been found
//
if (!fLocatedDeviceInstance) {
delete pBufDevice;
pBufDevice = NULL;
}
}
else {
//
// Not ours , but we are watching it - send refresh message to reread device list.
//
if (IsPlatformNT() ) {
fNeedReenumerateDeviceList = TRUE;
}
} // endif IsStillImageDevNode
}
//
// Process device event
//
lRet = NOERROR;
switch(DeviceEvent)
{
case DBT_DEVICEARRIVAL:
/*
if (fLocatedDeviceInstance && pBufDevice) {
PostMessage(hwnd,STIMON_MSG_ADD_DEVICE,1,(LPARAM)pBufDevice);
}
else {
//
// Just refresh active list
//
fNeedReenumerateDeviceList = TRUE;
//
//::PostMessage(g_hStiServiceWindow,
// STIMON_MSG_REFRESH,
// STIMON_MSG_REFRESH_REREAD,
// STIMON_MSG_REFRESH_NEW
// );
}
*/
g_pDevMan->ProcessDeviceArrival();
break;
case DBT_DEVICEQUERYREMOVE:
if ( fLocatedDeviceInstance && (pDev->dbch_devicetype == DBT_DEVTYP_HANDLE )) {
//
// This is targeted query - remove. We should disable PnP and device notifications and
// close interface handle immediately and then wait for remove - complete.
//
// NOTE: We always close and remove the device here, since it's the safest. If
// we wait till REMOVECOMPLETE, it may be too late.
//
#ifdef USE_POST_FORPNP
PostMessage(hwnd,STIMON_MSG_REMOVE_DEVICE,1,(LPARAM)pBufDevice);
#else
lRet = ::SendMessage(hwnd,STIMON_MSG_REMOVE_DEVICE,1,(LPARAM)pBufDevice);
#endif
}
break;
case DBT_DEVICEQUERYREMOVEFAILED:
if ( fLocatedDeviceInstance && (pDev->dbch_devicetype == DBT_DEVTYP_HANDLE )) {
//
// This is targeted query - remove - failed. We should reenable PnP notifications
//
// BUGBUG For now nothing to do as device is gone .
}
break;
case DBT_DEVICEREMOVEPENDING:
if (fLocatedDeviceInstance) {
//
// Added here for NT, as REMOVECOMPLETE comes too late
//
#ifdef USE_POST_FORPNP
PostMessage(hwnd,STIMON_MSG_REMOVE_DEVICE,1,(LPARAM)pBufDevice);
#else
lRet = ::SendMessage(hwnd,STIMON_MSG_REMOVE_DEVICE,1,(LPARAM)pBufDevice);
#endif
}
break;
case DBT_DEVICEREMOVECOMPLETE:
//
// For Windows 9x we can immediately remove device , as we don't have handle based
// notifications.
// On NT we should do that for handle based notifications only
//
fNeedReenumerateDeviceList = TRUE;
if ( fLocatedDeviceInstance &&
( (pDev->dbch_devicetype == DBT_DEVTYP_HANDLE ) ||
(pDev->dbch_devicetype == DBT_DEVTYP_DEVNODE )
)
) {
//
// We 've got targeted remove complete - get rid of device structures
//
if ( fLocatedDeviceInstance ) {
//
// Immediately remove device, as PnP counts handles and expects everyting
// to be cleaned up when this message handler returns.
//
lRet = ::SendMessage(hwnd,STIMON_MSG_REMOVE_DEVICE,FALSE,(LPARAM)pBufDevice);
fNeedReenumerateDeviceList = FALSE;
}
else {
//
// Bad thing happened - we really need to have active device when receiving notifications for it
//
ASSERT(("WM_DEVICECHANGE/REMOVE_COMPLETE/HANDLE No device found", 0));
}
}
else {
//
// Update info set as device is really destroyed now .
//
if (g_pDeviceInfoSet) {
g_pDeviceInfoSet->ProcessDeleteDeviceChangeMessage(lParam);
}
fNeedReenumerateDeviceList = TRUE;
}
break;
case DBT_DEVICETYPESPECIFIC:
break;
case DBT_DEVNODES_CHANGED:
if (g_fFirstDevNodeChangeMsg) {
//
// DevNodes have been modified in some way, so safest thing
// is to refresh the device list
//
fNeedReenumerateDeviceList = TRUE;
//
// Set flag to indicate that the first devnode change message
// after returning from standby has been handled
//
g_fFirstDevNodeChangeMsg = FALSE;
}
break;
default:
lRet = ERROR_INVALID_PARAMETER;
}
if ( fNeedReenumerateDeviceList ) {
//
// Purge the whole list , as this is the most reliable way to eliminate inactive devices
// Broadcast device removal here, as only now we know for sure device is gone.
//
// Nb: when we get this message, PnP on NT won't give us device name , thus reenumeration
// is required to clean up
//
::PostMessage(g_hStiServiceWindow,
STIMON_MSG_REFRESH,
STIMON_MSG_REFRESH_REREAD,
STIMON_MSG_PURGE_REMOVED | STIMON_MSG_REFRESH_NEW );
}
return (lRet == NOERROR) ? TRUE : FALSE;
}
//
// Guard to avoid reentrance in refresh routine
//
static LONG lInRefresh = 0L;
BOOL
OnDoRefreshActiveDeviceList(
WPARAM wParam,
LPARAM lParam
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
DBG_FN(OnDoRefreshActiveDeviceList);
DWORD dwParameter = MAKELONG(wParam,lParam);
#ifdef REFRESH_ASYNC
#ifdef USE_WORKER_THREAD
HANDLE hThread;
DWORD dwThread;
hThread = ::CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ConfigChangeThread,
(LPVOID)ULongToPtr(dwParameter),
0,
&dwThread);
if ( hThread )
CloseHandle(hThread);
#else
//
// Try to schedule refresh work item.
// This code will not work in case of suspending, because processing work items is stopped
//
ASSERT(("Suspending should not call schedule work item routine",
(wParam == STIMON_MSG_REFRESH_SUSPEND)));
DWORD dwSchedulerCookie = ::ScheduleWorkItem(
(PFN_SCHED_CALLBACK) ConfigChangeThread,
(LPVOID)dwParameter,
REFRESH_DELAY );
if ( dwSchedulerCookie ){
}
else {
ASSERT(("Refresh routine could not schedule work item", 0));
::WaitAndYield(::GetCurrentProcess(), REFRESH_DELAY);
RefreshDeviceList(wParam,(WORD)lParam);
}
#endif
#else
if (InterlockedExchange(&lInRefresh,1L)) {
return 0;
}
ConfigChangeThread((LPVOID)dwParameter);
InterlockedExchange(&lInRefresh,0L);
#endif
return TRUE;
}
VOID
ConfigChangeThread(
LPVOID lpParameter
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
ULONG ulParam = PtrToUlong(lpParameter);
WORD wCommand = LOWORD(ulParam);
WORD wFlags = HIWORD(ulParam);
DWORD dwWait = 0;
DBG_FN(ConfigChangeThread);
#ifdef DELAY_ON_BOOT
STIMONWPRINTF(TEXT("Refreshing device list. Command:%d Flags :%x"),wCommand,wFlags);
//
// On boot refresh - delay updating device list , to keep some devices happy
//
if (wFlags & STIMON_MSG_BOOT ) {
DBG_TRC(("Delaying refresh on boot "));
::Sleep(BOOT_REFRESH_DELAY);
}
#endif
//
// Wait for any pending refreshes to happen first. Only do the refresh once the other
// has completed.
// NOTE: For now, we always do the refresh - maybe we should skip if we timeout?
// One exception is if this is the first device enumeration (indicated by STIMON_MSG_BOOT)
// In this case, set the event so we don't timeout (the event is created unsignalled
// so WIA clients will wait on it).
//
if (!(wFlags & STIMON_MSG_BOOT)) {
dwWait = WaitForSingleObject(g_hDevListCompleteEvent, DEVICE_REFRESH_WAIT_TIME);
if (dwWait == WAIT_TIMEOUT) {
DBG_WRN(("::ConfigChangeThread, timed out while waiting for device list enumeration..."));
}
}
RefreshDeviceList(wCommand,wFlags);
//
// Update service status when necessary. If we are going to suspend - now it's time
// to let SCM know we are paused. Note, that it might be not only result of power management
// operation, but service pausing for any other reason.
//
if ( wCommand == STIMON_MSG_REFRESH_SUSPEND) {
// BUGBUG Service status should be updated only after everything paused
UpdateServiceStatus(SERVICE_PAUSED,NOERROR,0);
}
}
BOOL
OnAddNewDevice(
DEVICE_BROADCAST_INFO *psDevBroadcastInfo
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
USES_CONVERSION;
BOOL fRet;
fRet = (psDevBroadcastInfo != NULL) && (psDevBroadcastInfo->m_strDeviceName.GetLength() > 0);
if (fRet) {
//
// Create new device and add to the monitored list
//
DBG_TRC(("New device (%S) is being added to the list after PnP event",(LPCTSTR)psDevBroadcastInfo->m_strDeviceName));
fRet = AddDeviceByName((LPCTSTR)psDevBroadcastInfo->m_strDeviceName,TRUE);
// If device successfully recognized - broadcast it's appearance
if (fRet) {
BroadcastSTIChange(psDevBroadcastInfo,TEXT("STI\\Arrival\\"));
}
//
// Send delayed refresh message to pick up registry changes, happening in parallel
//
//
::PostMessage(g_hStiServiceWindow,
STIMON_MSG_REFRESH,
STIMON_MSG_REFRESH_REREAD,
STIMON_MSG_REFRESH_EXISTING | STIMON_MSG_REFRESH_NEW
);
//
// Fire the WIA device arrival event
//
if (psDevBroadcastInfo) {
// DBG_TRC(("WIA FIRE OnAddNewDevice : for device "));
//
// NotifyWiaDeviceEvent(T2W((LPTSTR)(LPCTSTR)psDevBroadcastInfo->m_strDeviceName),
// &WIA_EVENT_DEVICE_CONNECTED,
// NULL,
// 0,
// g_dwMessagePumpThreadId);
}
}
else {
DBG_ERR(("DevNode appears to be invalid, could not locate name"));
#ifdef WINNT
//
// Temporarily for NT make refresh , looking for new devices
//
::PostMessage(g_hStiServiceWindow,
STIMON_MSG_REFRESH,
STIMON_MSG_REFRESH_REREAD,
STIMON_MSG_REFRESH_NEW
);
#endif
}
return fRet;
}
BOOL
OnRemoveActiveDevice(
DEVICE_BROADCAST_INFO *psDevBroadcastInfo,
BOOL fRebroadcastRemoval
)
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
DBG_FN(OnRemoveActiveDevice);
USES_CONVERSION;
BOOL fRet;
fRet = (psDevBroadcastInfo != NULL) && (psDevBroadcastInfo->m_strDeviceName.GetLength() > 0);
DBG_TRC(("OnRemoveActiveDevice : Entering for device (%S) ",
fRet ? (LPCTSTR)psDevBroadcastInfo->m_strDeviceName : TEXT("<Invalid>")));
if (fRet) {
if (fRebroadcastRemoval) {
BroadcastSTIChange(psDevBroadcastInfo,TEXT("STI\\Removal\\"));
}
DBG_TRC(("Device (%S) is being removed after PnP event",(LPCTSTR)psDevBroadcastInfo->m_strDeviceName));
//
// Mark the device as being removed
//
MarkDeviceForRemoval((LPTSTR)(LPCTSTR)psDevBroadcastInfo->m_strDeviceName);
//
// Fire the WIA device removal event
//
if (psDevBroadcastInfo) {
DBG_TRC(("WIA FIRE OnRemoveActiveDevice : for device %S", psDevBroadcastInfo->m_strDeviceName));
NotifyWiaDeviceEvent((LPWSTR)T2W((LPTSTR)(LPCTSTR)psDevBroadcastInfo->m_strDeviceName),
&WIA_EVENT_DEVICE_DISCONNECTED,
NULL,
0,
g_dwMessagePumpThreadId);
}
fRet = RemoveDeviceByName((LPTSTR)(LPCTSTR)psDevBroadcastInfo->m_strDeviceName);
}
else {
DBG_ERR(("DevNode appears to be invalid, could not locate name"));
#ifdef WINNT
//
// Temporarily for NT make refresh , looking for new devices
//
::PostMessage(g_hStiServiceWindow,
STIMON_MSG_REFRESH,
STIMON_MSG_REFRESH_REREAD,
STIMON_MSG_REFRESH_EXISTING | STIMON_MSG_REFRESH_NEW
);
#endif
}
return fRet;
}
VOID
BroadcastSTIChange(
DEVICE_BROADCAST_INFO *psDevBroadcastInfo,
LPTSTR lpszStiAction
)
/*++
Routine Description:
Rebroadcasts STI specific device change message.
This is done so that STI client applications have a way to update their
device information without resorting to complicated PnP mechanism .
Device name and action is broadcasted
Arguments:
psDevBroadcastInfo - structure describing broadcast
lpszStiAction - string , encoding action, performed on a device
Return Value:
None.
--*/
{
USES_CONVERSION;
#ifdef USE_BROADCASTSYSTEM
DBG_FN(BroadcastSTIChange);
struct _DEV_BROADCAST_USERDEFINED *pBroadcastHeader;
StiCString strDeviceAnnouncement;
PBYTE pBroadcastString = NULL;
UINT uiBroadcastBufSize;
HANDLE hThread;
DWORD dwThread;
strDeviceAnnouncement.CopyString(lpszStiAction);
strDeviceAnnouncement+=psDevBroadcastInfo->m_strDeviceName;
uiBroadcastBufSize = sizeof(*pBroadcastHeader) + strDeviceAnnouncement.GetAllocLength();
pBroadcastString = new BYTE[uiBroadcastBufSize];
pBroadcastHeader =(struct _DEV_BROADCAST_USERDEFINED *)pBroadcastString;
if (pBroadcastHeader) {
pBroadcastHeader->dbud_dbh.dbch_reserved = 0;
pBroadcastHeader->dbud_dbh.dbch_devicetype = DBT_DEVTYP_OEM;
lstrcpyA(pBroadcastHeader->dbud_szName,T2A((LPTSTR)(LPCTSTR)strDeviceAnnouncement));
pBroadcastHeader->dbud_dbh.dbch_size = uiBroadcastBufSize;
DBG_TRC(("Broadcasting STI device (%S) action (%S)",
pBroadcastHeader->dbud_szName,
lpszStiAction));
hThread = ::CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)BroadcastSysMessageThreadProc,
(LPVOID)pBroadcastString,
0,
&dwThread);
if ( hThread )
CloseHandle(hThread);
}
#endif // USE_BROADCASTSYSTEM
} // endproc
VOID
BroadcastSysMessageThreadProc(
VOID *pContext
)
/*++
Routine Description:
Arguments:
--*/
{
DWORD dwStartTime = ::GetTickCount();
DWORD dwRecepients = BSM_APPLICATIONS
// | BSM_ALLDESKTOPS
// | BSM_ALLCOMPONENTS
;
struct _DEV_BROADCAST_USERDEFINED *pBroadcastHeader =
(_DEV_BROADCAST_USERDEFINED *) pContext;
::BroadcastSystemMessage(BSF_FORCEIFHUNG | BSF_NOTIMEOUTIFNOTHUNG |
BSF_POSTMESSAGE | BSF_IGNORECURRENTTASK,
&dwRecepients, // Broadcast to all
WM_DEVICECHANGE,
DBT_USERDEFINED, // wParam
(LPARAM)pBroadcastHeader // lParam
);
DBG_TRC((" Broadcasted system message for (%S). Taken time (ms): %d ",
pBroadcastHeader->dbud_szName,
(::GetTickCount() - dwStartTime)
));
delete[] (BYTE *) pContext;
return;
}
VOID
DumpDeviceChangeData(
HWND hWnd,
WPARAM wParam,
LPARAM lParam
)
/*++
Loads the window position structure from registry and resets
Returns:
Win32 error code. NO_ERROR on success
--*/
{
#ifdef MAXDEBUG
TCHAR szDbg[MAX_PATH];
OutputDebugString(TEXT("STISvc: WM_DEVICECHANGE message received\n"));
switch (wParam) {
case DBT_DEVICEARRIVAL:
OutputDebugString(TEXT(" DBT_DEVICEARRIVAL event\n"));
break;
case DBT_DEVICEREMOVECOMPLETE:
OutputDebugString(TEXT(" DBT_DEVICEREMOVECOMPLETE event\n"));
break;
case DBT_DEVICEQUERYREMOVE:
OutputDebugString(TEXT(" DBT_DEVICEQUERYREMOVE event\n"));
break;
case DBT_DEVICEQUERYREMOVEFAILED:
OutputDebugString(TEXT(" DBT_DEVICEQUERYREMOVEFAILED event\n"));
break;
case DBT_DEVICEREMOVEPENDING:
OutputDebugString(TEXT(" DBT_DEVICEREMOVEPENDING event\n"));
break;
case DBT_DEVICETYPESPECIFIC:
OutputDebugString(TEXT(" DBT_DEVICETYPESPECIFIC event\n"));
break;
case DBT_CUSTOMEVENT:
OutputDebugString(TEXT(" DBT_CUSTOMEVENT event\n"));
break;
case DBT_CONFIGCHANGECANCELED:
OutputDebugString(TEXT(" DBT_CONFIGCHANGECANCELED event\n"));
break;
case DBT_CONFIGCHANGED:
OutputDebugString(TEXT(" DBT_CONFIGCHANGED event\n"));
break;
case DBT_QUERYCHANGECONFIG:
OutputDebugString(TEXT(" DBT_QUERYCHANGECONFIG event\n"));
break;
case DBT_USERDEFINED:
OutputDebugString(TEXT(" DBT_USERDEFINED event\n"));
break;
default:
CHAR szOutput[MAX_PATH];
sprintf(szOutput, " DBT_unknown event, value (%d)\n", wParam);
OutputDebugStringA(szOutput);
break;
}
if (!lParam || IsBadReadPtr((PDEV_BROADCAST_HDR)lParam,sizeof(DEV_BROADCAST_HDR))) {
return ;
}
switch (((PDEV_BROADCAST_HDR)lParam)->dbch_devicetype) {
case DBT_DEVTYP_DEVICEINTERFACE: {
PDEV_BROADCAST_DEVICEINTERFACE p = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
TCHAR * pString;
OutputDebugString(TEXT(" DBT_DEVTYP_DEVICEINTERFACE\n"));
wsprintf(szDbg, TEXT(" %s\n"), p->dbcc_name);
OutputDebugString(szDbg);
if (UuidToString(&p->dbcc_classguid, (RPC_STRING* )&pString) == RPC_S_OK)
{
wsprintf(szDbg, TEXT(" %s\n"), pString);
OutputDebugString(szDbg);
RpcStringFree((RPC_STRING* )&pString);
}
break;
}
case DBT_DEVTYP_HANDLE:
OutputDebugString(TEXT(" DBT_DEVTYP_HANDLE\n"));
break;
default:
break;
}
wsprintf(szDbg, TEXT(" wParam = %X lParam=%X\n"),wParam,lParam);
OutputDebugString(szDbg);
#endif
} // DumpDeviceChangeData