从 .NET 调用非托管代码以及使用 C#.NET 进行设备检测






4.12/5 (10投票s)
一篇探讨硬件插入或移除的文章
引言
在 Windows 编程中,接收“硬件插入或移除”是一个有趣的事情。 事实上,我们可能已经在我们的 MFC 或 SDK 应用程序中做过这件事。 正如我之前的文章的一些读者建议的那样,我尝试在这里提交一篇关于 C#.NET 中设备检测的文章。 本文的目的是分享我从网络上其他来源找到的概念。 我希望这篇文章能满足我的意图。
从 .NET 调用非托管代码
我们已经知道,.NET Framework 是一个开发和执行环境,它允许不同的编程语言和库无缝地协同工作,以创建基于 Windows 的应用程序。
尽管 .NET Framework 具有广泛性和深度,但它不能做所有事情。 有时你需要做一些 Framework 没有涵盖的事情。
这种情况可能发生在许多场景中,但最常见的两个场景是
- 当你知道某些功能是 Windows API 的一部分,但尚未包含在 .NET Framework 中时。
- 当你正在编写一个外部设备,其驱动程序是为非 .NET 程序员编写的时。
在这两种情况下,我们的程序都需要调用非托管代码。 更具体地说,它需要调用作为动态链接库或 DLL 的一部分提供的代码;第一种情况是 Windows DLL,第二种情况是制造商提供的 DLL。
基本上,在运行时控制下执行的代码称为托管代码。 相反,在运行时之外运行的代码称为非托管代码。 COM 组件、ActiveX 接口和 Win32 API 函数都是非托管代码的示例。
我们如何从你的托管代码 .NET 程序中调用非托管代码 DLL 中的函数? 这在 .NET 中通过一个称为“互操作性”的概念来实现。
互操作服务
此命名空间提供了广泛的成员,支持 COM 互操作和平台调用服务。 这是我们在 C#.NET 中实现设备检测的关键。
此命名空间中存在的 DllImport 关键字用于将上下文的非托管 DLL 导入到我们的 .NET 应用程序中。
如果我们知道非托管 DLL 中存在的函数名称和常量,我们可以将它们全部导入到我们的 .NET 应用程序中。
例如,我将使用 Windows Sleep() 函数,它具有以下声明(在 C 中)
BOOL Sleep(
        DWORD dwDuration    
//Duration in milliseconds
};
为了使这个函数在你的托管代码中可以访问,你可以这样编写 DllImport 语句
[DllImport("Kernel32.dll")]
    static extern Boolean Sleep( 
       UInt32 duration);
通过我们的托管代码中的这个 DllImport 语句,我们可以像调用任何其他函数一样调用 Sleep 函数
Sleep(1000);
C# 中的设备检测
每当发生设备到达/移除时,Windows 会向系统中当前运行的所有应用程序发送一条名为 WM_DEVICECHANGE 的消息。 但是要接收此消息,我们的应用程序应该处理“Windows 进程函数”。 C# 应用程序将不会默认支持此函数;我们需要在我们的 Form 类中显式覆盖此方法。
如果我们想找到插入的设备类型(特别是 USB 大容量存储),那么我们需要包含在非托管区域中定义的结构。
为此,我们需要进行封送处理。 封送处理是一个单独的主题,网上有很多关于这个主题的文章。 所以我只是简单地展示一个例子。
[StructLayout(LayoutKind.Sequential)] 
public struct DEV_BROADCAST_VOLUME 
{ 
    public int dbcv_size; 
    public int dbcv_devicetype; 
    public int dbcv_reserved; 
    public int dbcv_unitmask; 
} 
//Use the Interopservices to make the Unmanaged call.
using System.Runtime.InteropServices;
protected override void WndProc(ref Message m) 
{ 
    //you may find these definitions in dbt.h and winuser.h 
    const int WM_DEVICECHANGE = 0x0219; 
    // system detected a new device 
    const int DBT_DEVICEARRIVAL = 0x8000;
    // system detected a new device 
    const int DBT_DEVICEREMOVECOMPLETE = 0x8001;    
    // logical volume 
    const int DBT_DEVTYP_VOLUME = 0x00000002;  
    switch(m.Msg)
    {
    case WM_DEVICECHANGE:
        switch(m.WParam.ToInt32())
        {
        case DBT_DEVICEARRIVAL:
            { 
                int devType = Marshal.ReadInt32(m.LParam,4); 
                if(devType == DBT_DEVTYP_VOLUME) 
                { 
                    DEV_BROADCAST_VOLUME vol; 
                    vol = (DEV_BROADCAST_VOLUME)
                        Marshal.PtrToStructure(
                        m.LParam,typeof(DEV_BROADCAST_VOLUME)); 
                    MessageBox.Show(
                        vol.dbcv_unitmask.ToString("x"));
                } 
            } 
            break;
        case DBT_DEVICEREMOVECOMPLETE:
            MessageBox.Show("Removal");
            break;
        }
        break;
    }
    //we detect the media arrival event 
    base.WndProc (ref m); 
}
构建示例代码
这是一个简单的 C# Windows 窗体应用程序。 构建示例代码需要 Visual Studio.NET 2003 编译器。 执行应用程序并插入/移除 USB 大容量存储。 将出现设备到达和移除的消息框。


