介绍
这篇文章将示范:
- 怎样使用P/Invoke 访问非托管代码。
- 怎样使用Spy++将Windows消息载入日志,获取参数wParam,lParam的值。
- 怎样用C#实现FindWindow( )方法和SendMessage( )方法。
- 怎样和Windows Media Player互操作。
关于Win32 APIs
MSDN对Windows API的定义是这样的:
Windows API(Application Programming Interface 应用程序接口)由函数,消息,数据结构,数据类型和对它们的描述组成,可以使用这些创建在Windows环境下运行的应用程序。API 中使用最多的部分是通过Windows(操作系统)调用API函数的代码,包括: 对Windows函数的必要的声明,用户自定义的类型(作为数据结构传递给Windows函数),还有常量的声明(作为传递给那些函数或者它们的返回值)。
简单的说,Windows APIs就是嵌入微软Windows操作系统的动态链接库。因为这些库中包含了数以千计的有用的函数,因此如果在我们的代码中使用Window APIs,将大大减少开发的时间和复杂度。但这样做也有不利之处:Window APIs没有使用托管代码,而且有许多不同于Visual Studio.NET中的数据类型。.NET框架已经为我们将一部分重要的Win32 APIs包装为托管代码,而剩下的那些则需要我们在平台调用服务(Platform Invocation Service,一下称为P/Invoke)中编写托管代码来调用了。
关于平台调用服务(Platform Invocation Services,P/Invoke)
P/Invoke是这样一种机制:通过它,托管代码可以调用DLL中实现的那些非托管函数。
当调用非托管函数时,P/Invoke必须知道动态链接库的名字和它所包含的函数(或者序号)。DllImport属性用来指定DLL的位置(包含某些外部方法的实现的DLL),而该属性的参数用来详细说明一些细节,比如EntryPoint和CharSet。获得更多关于DllImport属性的信息,请参考MSDN中的DllImportAttribute类。
表1:Win32 API中常用的DLL
DLL | 描述 |
Kernel32.dll | 包含一些关于内存管理和资源处理的底层函数 |
GDI32.dll | 包含设备输出的一些图形设备接口,比如管理绘图和字体的一些接口 |
User32.dll | 包含Windows处理消息、计时、菜单、通信的函数 |
使用C# 调用Windows API的步骤
- 导入System.Runtime.InteropServices 名称空间
- 用DllImport定义函数
- 增加编码调用Win32 API
我们将通过下面的Win32类来说明怎样实现和User32.dll中的API函数有关系的FindWindow( )、SendMessage( )。
... {
//当用户从菜单中选择一条命令,
//一个控件向它的父窗口发送通知消息,
//或者快捷键被按下时,
//将发送WM_COMMAND消息
public const int WM_COMMAND = 0x111;
// FindWindow 函数可找到一个类名、窗口名
//与指定字符串相匹配的最上层的窗口的句柄
// 该函数不搜索子窗口.
// 该函数的搜索过程中不区分大小写.
[DllImport("User32.dll")]
public static extern int FindWindow(string strClassName,
string strWindowName);
// FindWindow 函数可找到一个类名、窗口名
//与指定字符串相匹配的最上层的窗口的句柄
// 该函数从制定的子窗口起依次搜索子窗口
// 该函数的搜索过程中不区分大小写.
[DllImport("User32.dll")]
public static extern int FindWindowEx(int hwndParent,
int hwndChildAfter, string strClassName, string strWindowName);
// SendMessage 函数给一个或几个窗口发送特定的消息
// 特定的窗口通过该函数调用程序段,直到处理消息后返回.
[DllImport("User32.dll")]
public static extern Int32 SendMessage(
int hWnd, // 目标窗口句柄
int wParam, // 消息的前一个附加参数
[MarshalAs(UnmanagedType.LPStr)] string lParam);
// 消息的第二个附加参数
[DllImport("User32.dll")]
public static extern Int32 SendMessage(
int hWnd, // 目标窗口的句柄
public Win32()
...{
}
~Win32()
...{
}
}
Windows Media Player 的交互操作概念:
需要:
- Microsoft Visual Studio .NET
- Microsoft Spy++
- Windows Media Player
- 打开Spy++。
- 打开Windows Media Player,拖动窗口使Media Player 和Spy++同时可见。
- 确保Windows Media Player 没有使用“自动隐藏菜单栏”属性。
- 在工作区中选择窗口“Windows Media Player”后按下Log Messages 按钮,或者按快捷键“Ctrl+M”。
接下来,在“Message Options”对话框中切换到Messages标签页,清除所有选中的消息,在显示消息的列表框中向下滚动,选中“WM_COMMAND”,然后单击OK确定。
接下来,在Windows Media Player的菜单“Play”下选择“Stop”命令,或者按快捷键“Ctrl+S”(确保Player先前处于播放状态),Spy++ 将会记录WM_COMMAND消息。
Spy++的查看消息窗口将如下图所示。请注意,第一列显示了窗口的句柄,第二列显示了这条消息的编码,而剩下的部分则显示了消息的参数和返回值。
在这条消息上单击右键选择“属性”,就可以查看该消息的附加信息,比如wParam和lParam的十六进制值。
对于Windows Media Player中“Play”命令的消息,也可以通过重复以上步骤得到:
使用P/Invoke和Spy++,很容易扩展包括其他一些操作的功能,比如切换、重复、调整音量等,但作为示例,我们只实现 播放/暂停 和 停止。
private void btnStop_Click( object sender, System.EventArgs e)
... {
Win32.SendMessage(iHandle, Win32.WM_COMMAND, 0x00004979, 0x00000000);
}
private void btnPlayPause_Click( object sender, System.EventArgs e)
... {
Win32.SendMessage(iHandle, Win32.WM_COMMAND, 0x00004978, 0x00000000);
}
private void MainForm_Load( object sender, System.EventArgs e)
... {
// 获得窗口句柄
iHandle = Win32.FindWindow("WMPlayerApp", "Windows Media Player");
}
结束语
我写这篇文章只是兴趣所在,尽管这不是什么新颖的想法,但我还是希望有人能发现它的用处。开始,我打算深入的研究Windows Media Player,通过FindWindowEx( )函数和ATL:SysListView32中的“Current Playlist”,在远程机器上使用WMI接口操作本机Windows Media Player的播放列表。如果谁有好的意见或者独特的想法,请联系我。
作者简介
Alexander Kent | Biography in progress ;o)
|