2024年3月21日发(作者:)

WM_

==========================================

(一)、WM_DEVICECHANGE 消息的接收解决方法:

一般WM_DEVICECHANGE只发给顶层窗口。你可以自己创建一个隐藏的顶层窗口来接收这个消息。

在没有顶层窗口时,如OCX里或子窗体里,经过我测试,使用RegisterDeviceNotification后,

便可以接受到(所设置的GUID设备的)消息了。

==========================================

(二)、在VC6中,一般要手动添加这个消息的映射代码,分三步,过程如下:

第1步:在窗口类的.h文件中增加:

.........

afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD dwData);

DECLARE_MESSAGE_MAP()

第2步:在窗口类的.cpp文件中增加:

ON_WM_DEVICECHANGE()

END_MESSAGE_MAP()

第3步:在窗口类的.cpp文件中增加:

BOOL CClassCtrl::OnDeviceChange(UINT nEventType, DWORD dwData)

{

return (TRUE); // 返回TRUE表示不拒绝此操作

}

==========================================

(三)在OnDeviceChange的参数中的nEventType,为如下状态定义:

#define DBT_DEVICEARRIVAL 0x8000 // system detected a new device

#define DBT_DEVICEQUERYREMOVE 0x8001 // wants to remove, may fail

#define DBT_DEVICEQUERYREMOVEFAILED 0x8002 // removal aborted

#define DBT_DEVICEREMOVEPENDING 0x8003 // about to remove, still avail.

#define DBT_DEVICEREMOVECOMPLETE 0x8004 // device is gone

#define DBT_DEVICETYPESPECIFIC 0x8005 // type specific event

#if(WINVER >= 0x040A)

#define DBT_CUSTOMEVENT 0x8006 // user-defined event

#endif /* WINVER >= 0x040A */

#define DBT_DEVTYP_DEVICEINTERFACE 0x00000005

#define DBT_DEVTYP_HANDLE 0x00000006

#define DBT_DEVTYP_OEM 0x00000000

#define DBT_DEVTYP_PORT 0x00000003

#define DBT_DEVTYP_VOLUME 0x00000002

==========================================

(四)在OnDeviceChange的参数中的dwData,一般为如下状态定义:

PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)dwData;

if(pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)

{

PDEV_BROADCAST_DEVICEINTERFACE pInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;

// pInf指向设备相关的信息结构体,参见以下节中的定义

}

==========================================

(五)由WM_DEVICECHANGE可以获取设备的GUID及设备名(可能含有VID-PID及SN值)

typedef struct _DEV_BROADCAST_HDR {

DWORD dbch_size;

DWORD dbch_devicetype;

DWORD dbch_reserved;

} DEV_BROADCAST_HDR, *PDEV_BROADCAST_HDR;

typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A {

DWORD dbcc_size;

DWORD dbcc_devicetype;

DWORD dbcc_reserved;

GUID dbcc_classguid;

char dbcc_name[1];

第 1 页

WM_

} DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;

dbcc_classguid 类似于:{A5DCBF10-6530-11D2-901F-00C04FB951ED}

dbcc_name 类似于:?USB#Vid_0c45&Pid_62f1#5&2b0c8088&0&4#{a5dcbf10-6530-11d2-901f-00c04fb951ed}

==========================================

(六)下面是一些常用的设备的class的GUID举例:

// 65E8773D-8F56-11D0-A3B9-00A0C9223196

OUR_GUID_ENTRY(AM_KSCATEGORY_CAPTURE, // 摄像头捕捉的class GUID

0x65E8773DL, 0x8F56, 0x11D0, 0xA3, 0xB9, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96)

GUID usb_guid={0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED};

GUID hid_guid={0x4D1E55B2,0xF16F,0x11CF,0x88,0xCB,0x00,0x11,0x11,0x00,0x00,0x30};

USB Raw Device/USB设备

{a5dcbf10-6530-11d2-901f-00c04fb951ed}

Disk Device/磁盘设备

{53f56307-b6bf-11d0-94f2-00a0c91efb8b}

Network Card/网卡

{ad498944-762f-11d0-8dcb-00c04fc3358c}

Human Interface Device (HID)/人机界面设备

{4d1e55b2-f16f-11cf-88cb-}

Palm/手持设备

{784126bf-4190-11d4-b5c2-00c04f687a67}

DEFINE_GUID(UsbClassGuid, 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);

DEFINE_GUID(DiskClassGuid, 0x53F56307, 0xB6BF, 0x11D0, 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B);

DEFINE_GUID(CdRomClassGuid, 0x53F56308, 0xB6BF, 0x11D0, 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B);

==========================================

(七)如何“注册”设备的classGUID对应的设备事件通知(WINVER 0x0500 方法):

只有注册了该设备,OnDeviceChange才能获得详细的信息,否则收到的参数都是0007.

#include

DEV_BROADCAST_DEVICEINTERFACE dbi;

ZeroMemory(&dbi,sizeof(dbi));

_size = sizeof(dbi);

_devicetype = DBT_DEVTYP_DEVICEINTERFACE;

_reserved = 0;

_classguid = hid_guid;

HDEVNOTIFY hDevNotify;

hDevNotify = RegisterDeviceNotification(m_hWnd,

&dbi, DEVICE_NOTIFY_WINDOW_HANDLE);

if(!hDevNotify)

{

int Err = GetLastError();

printf("RegisterDeviceNotification failed: %lx.n", Err);

return (FALSE);

}

注意:在程序结束前,一定要记得反注册:

UnregisterDeviceNotification(hDevNotify);

因默认VC6的WINVER宏定义是<0x0500的,故在必要时,在StdAfx.h中添加:

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

#define WINVER 0x0500

#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers

第 2 页

WM_

==========================================

(八)如何“注册”设备的classGUID对应的设备事件通知(动态加载-方法):

// 注册设备通知事件,须与反注册成对儿使用

PVOID RegistCapvNotice(HWND hWnd)

{

if(!hWnd || !::IsWindow(hWnd)) return (NULL);

HMODULE hModUser = GetModuleHandle(TEXT(""));

if(!hModUser) return (NULL);

PRegistDevNotify fnReg = (PRegistDevNotify)GetProcAddress(

hModUser, TEXT("RegisterDeviceNotificationA"));

if(!fnReg) return (NULL);

STDEVINTRF dbci;

ZeroMemory(&dbci, sizeof(STDEVINTRF));

_size = sizeof(STDEVINTRF);

_devicetype = 0x00000005;

_classguid = AM_KSCATEGORY_CAPTURE;

return (fnReg(hWnd, &dbci, 0x00000000));

}

// 取消注册设备通知(反注册),须与注册成对儿使用

BOOL UnRegistCapvNotice(PVOID hHandle)

{

if(!hHandle) return (TRUE);

HMODULE hModUser = GetModuleHandle(TEXT(""));

if(!hModUser) return (FALSE);

PUnRegistDevNotify fnUreg = (PUnRegistDevNotify)GetProcAddress(

hModUser, TEXT("UnregisterDeviceNotification"));

if(!fnUreg) return (FALSE);

return (fnUreg(hHandle));

}

注意:上述在注册的classguid为AM_KSCATEGORY_CAPTURE,视频捕捉设备,可按需更换。

==========================================

(九)在DirectShow里,可以响应EC_DEVICE_LOST事件,来响应视频设备的插拔,它有2参数:

lParam1 为(IUnknown*) Pointer,是描述当前设备的过滤器的。

lParam2 为0=设备已经移除,1=设备又重新有效了(又上了)

由于在移除后,设备过滤器不再有效,所以必须重建graph才行。

实现的过滤分以下几步:

1、在类的.h里,增加

IMediaEventEx *m_pEvent; // 过滤器事件通报

2、然后在创建IGraphBuilder后,获取m_pEvent接口:

hr = m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pEvent);

3、在设置视频窗口时,把事件的目标窗口设置为所需要的:

hr = m_pEvent->SetNotifyWindow((OAHWND)m_hVdad, WM_DXGRAPHNOTIFY, 0);

其中WM_DXGRAPHNOTIFY为窗口自定义消息,可以定义如下:

#define WM_DXGRAPHNOTIFY (WM_APP + 110)

4、然后在m_hVdad所在目标窗口类中,增加自定义消息WM_DXGRAPHNOTIFY的响应代码:

在.h中增加:

afx_msg LRESULT OnDshowNotifyForMain(WPARAM wParam, LPARAM lParam);

DECLARE_MESSAGE_MAP()

在.cpp中增加:

ON_MESSAGE(WM_DXGRAPHNOTIFY, OnDshowNotifyForMain)

END_MESSAGE_MAP()

LRESULT CClassCtrl::OnDshowNotifyForMain(WPARAM wParam, LPARAM lParam)

{

DoEvt(); return (0); // 增加自定义处理,DoEvt参见下面定义

}

第 3 页

WM_

5、DoEvt()里面需要使用m_pEvent接口,故m_pEvent可为全局量,为类中量DoEvt外用:

void CClass::DoEvt(void)

{

long evCode, param1, param2;

while(m_pEvent && SUCCEEDED(m_pEvent->GetEvent(

&evCode, ¶m1, ¶m2, 0))) // 遍历全部当前事件

{

m_pEvent->FreeEventParams(evCode, param1, param2);

if(evCode == EC_DEVICE_LOST && !param2) Close();

}

}

==========================================

(十)为了有效地判断视频的插拔,可以采用如下2种方法。

第1种为:只使用WM_DEVICECHANGE消息,注册AM_KSCATEGORY_CAPTURE视频设备通知。

这样可以获取到DEV_BROADCAST_DEVICEINTERFACE结构体,里面有dbcc_name设备名。

可以创建一个关于此dbcc_name设备的管理列表,响应插拔动作,与dbcc_name检查。

第2种为:对插和拔两个动作,分而治之:

新到:使用WM_DEVICECHANGE消息来,获取设备插上的消息,打开新到设备。

移除:再使用EC_DEVICE_LOST消息,来响应对应某个设备的移除。

这是由于,EC_DEVICE_LOST在没有建立有效的Graph时,新到的设备,它是无法感知的。

EC_DEVICE_LOST只能是graph建立后,感知设备的移除,和它再次插入。

所以,EC_DEVICE_LOST感知移除是很有效和针对性的,

再配合WM_DEVICECHANGE来判断新来插入的设备,再创建新的实例。

==========================================

#define WINVER 0x0500

#include

#define STD_RUI(type, fapi) extern "C" typedef type (__stdcall *P##fapi)

STD_RUI(PVOID, RegistDevNotify)(HWND hWnd, PVOID pFilter, DWORD dwFlag);

PVOID RegistCapvNotice(HWND hWnd);

// 注册设备通知事件

STD_RUI(BOOL, UnRegistDevNotify)(PVOID hHandle);

BOOL UnRegistCapvNotice(PVOID hHandle);

// 取消注册设备通知

GUID UsbClassGuid = {0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED};;

//GUID DiskClassGuid = {0x53F56307, 0xB6BF, 0x11D0, 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B};

/////////////////////////////////

//}}AFX_MSG

afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD dwData);

DECLARE_MESSAGE_MAP()

/////////////////////////////////

BEGIN_MESSAGE_MAP(CTestWinDlg, CDialog)

//{{AFX_MSG_MAP(CTestWinDlg)

ON_WM_SYSCOMMAND()

ON_WM_PAINT()

ON_WM_QUERYDRAGICON()

//}}AFX_MSG_MAP

ON_WM_DEVICECHANGE()

END_MESSAGE_MAP()

/////////////////////////////////

// 注册设备通知事件,须与反注册成对儿使用

PVOID RegistCapvNotice(HWND hWnd)

{

if(!hWnd || !::IsWindow(hWnd)) return (NULL);

HMODULE hModUser = GetModuleHandle(TEXT(""));

if(!hModUser) return (NULL);

PRegistDevNotify fnReg = (PRegistDevNotify)GetProcAddress(

第 4 页

WM_

hModUser, TEXT("RegisterDeviceNotificationA"));

if(!fnReg) return (NULL);

DEV_BROADCAST_DEVICEINTERFACE dbci;

ZeroMemory(&dbci, sizeof(DEV_BROADCAST_DEVICEINTERFACE));

_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);

_devicetype = 0x00000005;

_classguid = UsbClassGuid;

return (fnReg(hWnd, &dbci, 0x00000000));

}

// 取消注册设备通知(反注册),须与注册成对儿使用

BOOL UnRegistCapvNotice(PVOID hHandle)

{

if(!hHandle) return (TRUE);

HMODULE hModUser = GetModuleHandle(TEXT(""));

if(!hModUser) return (FALSE);

PUnRegistDevNotify fnUreg = (PUnRegistDevNotify)GetProcAddress(

hModUser, TEXT("UnregisterDeviceNotification"));

if(!fnUreg) return (FALSE);

return (fnUreg(hHandle));

}

BOOL CTestWinDlg::OnInitDialog()

{

CDialog::OnInitDialog();

// ...........

// TODO: Add extra initialization here

m_hDevNotify = RegistCapvNotice(m_hWnd); // 设备通知注册

return TRUE; // return TRUE unless you set the focus to a control

}

void CTestWinDlg::OnCancel()

{

UnregisterDeviceNotification(m_hDevNotify);

}

CDialog::OnCancel();

BOOL CTestWinDlg::OnDeviceChange(UINT nEventType, DWORD dwData)

{

PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)dwData;

if(pHdr && pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)

{

PDEV_BROADCAST_DEVICEINTERFACE pInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;

// pInf指向设备相关的信息结构体,参见以下节中的定义

TRACE("%sn", pInf->dbcc_name);

}

return (TRUE); // 返回TRUE表示不拒绝此操作

}

==========================================

sck007

2011.03.11

第 5 页