从 .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 大容量存储。 将出现设备到达和移除的消息框。