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

使用 P/Invoke 和 C# 与 Windows Media Player 互操作

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (51投票s)

2004年2月8日

CPOL

4分钟阅读

viewsIcon

337827

downloadIcon

6456

本文档演示了如何使用 Visual C# 中的 Platform Invoke Services 与 Windows Media Player 进行互操作。

引言

本文旨在演示

  • 如何使用 P/Invoke 调用非托管代码。
  • 如何使用 Spy++ 记录 Windows 消息并获取 wParamlParam 的值。
  • 如何在 C# 中实现 FindWindow()SendMessage()
  • 如何与 Windows Media Player 进行互操作。

Win32 API 简介

MSDN 对 Windows API 的定义是

Windows API(应用程序编程接口)包含您在创建运行于 Microsoft Windows 上的应用程序时可以使用到的函数、消息、数据结构、数据类型和语句。您最常使用的 API 部分是通过代码元素来从 Windows 调用 API 函数。这些包括过程声明(用于 Windows 函数)、用户定义类型定义(用于传递给这些函数的数据结构)以及常量声明(用于传递给这些函数以及从这些函数返回的值)。

简而言之,Windows API 是嵌入在 Microsoft Windows 操作系统中的 DLL。在代码中使用 Windows API 的优点是,由于它们已包含数百个有用的函数,可以减少开发时间和复杂性。缺点是 Windows API 不使用托管代码,并且其数据类型与 Visual Studio .NET 使用的数据类型不同。虽然 .NET Framework 已将大部分 Win32 API 封装到托管代码中,但那些仍然是非托管的且没有 .NET 等效项的 API 可以使用平台调用服务从托管代码调用。

平台调用服务简介

平台调用服务 (P/Invoke) 是一种托管代码调用实现于 DLL 中的非托管函数的机制。

P/Invoke

在调用非托管函数时,平台调用必须知道 DLL 文件名和函数或序号。DLLImport 属性用于指定包含外部方法实现的 DLL 位置,其参数用于指定特定的行为,例如 EntryPointCharSet。有关 DllImport 属性语法的更多信息,请参阅 MSDN 上的 DllImportAttribute 类。

表 1:Win32 API 中常用的 DLL

DLL

其内容描述

Kernel32.dll

用于内存管理和资源处理的低级操作系统函数。

GDI32.dll

图形设备接口 (GDI) 函数,用于设备输出,例如绘图和字体管理。

User32.dll

用于消息处理、计时器、菜单和通信的 Windows 管理函数。

使用 C# 调用 Win32 API 的步骤

  1. 导入 System.Runtime.InteropServices 命名空间。
  2. 使用 DLLImport 定义函数。
  3. 添加代码以调用 Win32 API。

我们将实现 User32.dll 中找到的 FindWindow()SendMessage() 相关的 Win32 API 函数,如下面的 Win32 类所示。

public class Win32
{
    // The WM_COMMAND message is sent when the user
    // selects a command item from a menu, 
    // when a control sends a notification message
    // to its parent window, or when an 
    // accelerator keystroke is translated.
    public const int WM_COMMAND = 0x111;

    // The FindWindow function retrieves a handle
    // to the top-level window whose class name
    // and window name match the specified strings.
    // This function does not search child windows.
    // This function does not perform a case-sensitive search.
    [DllImport("User32.dll")]
    public static extern int FindWindow(string strClassName, 
                                             string strWindowName);

    // The FindWindowEx function retrieves
    // a handle to a window whose class name 
    // and window name match the specified strings.
    // The function searches child windows, beginning
    // with the one following the specified child window.
    // This function does not perform a case-sensitive search.
    [DllImport("User32.dll")]
    public static extern int FindWindowEx(int hwndParent, 
        int hwndChildAfter, string strClassName, string strWindowName);


    // The SendMessage function sends the specified message to a 
    // window or windows. It calls the window procedure for the specified 
    // window and does not return until the window procedure
    // has processed the message. 
    [DllImport("User32.dll")]
    public static extern Int32 SendMessage(
        int hWnd,               // handle to destination window
        int Msg,                // message
        int wParam,             // first message parameter
        [MarshalAs(UnmanagedType.LPStr)] string lParam); 
                                // second message parameter

    [DllImport("User32.dll")]
    public static extern Int32 SendMessage(
        int hWnd,               // handle to destination window
        int Msg,                // message
        int wParam,             // first message parameter
        int lParam);            // second message parameter

    public Win32()
    {

    }

    ~Win32()
    {
    }
}

Windows Media Player 互操作概念图

Sample screenshot

要求

  • Microsoft Visual Studio .NET
  • Microsoft Spy++
  • Windows Media Player
  1. 打开 Spy++ 并按下“日志消息”按钮,或按下 Ctrl + M。
  2. 打开 Microsoft Windows Media Player,调整窗口布局,使 Spy++ 和 Windows Media Player 都可见。
  3. 确保 Windows Media Player 未使用“自动隐藏菜单栏”功能,并将查找工具拖到标题为“Windows Media Player”的外部 Windows Media Player 窗口上。

- 或 -

如果您知道 Windows Media Player 窗口的句柄,可以在“句柄”框中输入。

Step1

接下来,转到“消息”选项卡并清除所有选中的消息。在“要查看的消息”列表框中,向下滚动并选择 WM_COMMAND,然后单击“确定”。

此时,只选中 WM_COMMAND 将使下一步更容易。

Step2

接下来,从 Windows Media Player 主菜单中选择“播放”->“停止”,或者直接按下 Ctrl+S,以便 Spy++ 记录 WM_COMMAND 消息。

<00001> 0023017A P WM_COMMAND wNotifyCode:0 (sent from a menu) wID:18809

消息视图如下所示。请注意,第一列包含窗口句柄,第二列包含消息代码。消息参数和返回值在右侧。

Step3

双击此条目以查看额外的消息属性,例如 wParamlParam 的十六进制值。

Step4

对“开始”按钮重复上述操作得到

<00001> 0023017A P WM_COMMAND wNotifyCode:0 (sent from a menu) wID:18808

使用 P/Invoke 和 Spy++,可以轻松扩展功能以包含弹出、重复或音量控制等其他选项,但出于演示目的,我们将仅实现开始/暂停和停止。

private System.Int32 iHandle;

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)
{
    // get Window handle
    iHandle = Win32.FindWindow("WMPlayerApp", "Windows Media Player"); 
}

结论

我写这篇文章很快乐,即使它不是一个原创的想法,但希望有人会觉得它很有用。最初,我曾想深入研究 Windows Media Player,使用 FindWindowEx() 并开始使用“当前播放列表”的 ATL:SysListView32,通过 WMI 在选定的远程计算机上操作 Windows Media Player 播放列表。如果有人有其他想法或功能请求,请告诉我。

历史

  • 版本 1.0 - 2004 年 2 月 8 日 - 原始提交。
© . All rights reserved.