65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.12/5 (10投票s)

2007年4月11日

CPOL

3分钟阅读

viewsIcon

88857

downloadIcon

1159

一篇探讨硬件插入或移除的文章

引言

在 Windows 编程中,接收“硬件插入或移除”是一个有趣的事情。 事实上,我们可能已经在我们的 MFC 或 SDK 应用程序中做过这件事。 正如我之前的文章的一些读者建议的那样,我尝试在这里提交一篇关于 C#.NET 中设备检测的文章。 本文的目的是分享我从网络上其他来源找到的概念。 我希望这篇文章能满足我的意图。

从 .NET 调用非托管代码

我们已经知道,.NET Framework 是一个开发和执行环境,它允许不同的编程语言和库无缝地协同工作,以创建基于 Windows 的应用程序。

尽管 .NET Framework 具有广泛性和深度,但它不能做所有事情。 有时你需要做一些 Framework 没有涵盖的事情。

这种情况可能发生在许多场景中,但最常见的两个场景是

  1. 当你知道某些功能是 Windows API 的一部分,但尚未包含在 .NET Framework 中时。
  2. 当你正在编写一个外部设备,其驱动程序是为非 .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 大容量存储。 将出现设备到达和移除的消息框。

© . All rights reserved.