从外部应用程序显示 Microsoft Windows XP Media Center UI 中的对话框






4.67/5 (11投票s)
本文将介绍如何从外部应用程序在 Microsoft 的 Windows XP Media Center UI 中显示对话框。
引言
Windows XP Media Center Edition 在您家中的任何房间都能提供最佳的 Windows 体验,无论您是寻找家用电脑还是增强家庭影院。享受集成的家庭娱乐体验,包括照片、音乐、电视等。连接家中的设备以及移动设备,扩展您的娱乐体验(有关详细信息,请参阅 Microsoft 的 Media Center 网站)。
如果您想将您的应用程序集成到这个新的用户体验中,您可能想扩展您现有的应用程序,以便在用户观看电视时向其显示通知。本文介绍了一种向用户显示带图像的通知的简单方法。
背景
Microsoft 没有直接提供从外部应用程序在 Media Center UI 中显示通知的方法,但他们提供了另一种可能性:您可以向 Media Center 添加“后台插件”,这些插件会在 Media Center UI 运行时一直运行,并用于接收来自“外部世界”的消息。本文将介绍如何使用这些后台插件来提供一种向用户显示消息的简单方法。
Michael Creasy 描述了如何开发这些后台插件。在他的博客(参见 此处)中,您可以找到一个基本的后台插件示例。
为了与这个正在运行的 Media Center 插件进行通信,我使用了一个简单的 WM_COPYDATA
来将要显示和传递给插件的文本和其他信息发送过去。在两个不同的应用程序之间有几种通信方式,但如果您的程序部分是用不同的编程语言开发的,这种方式是最容易使用的。
为了概述所有组件和流程,我准备了以下流程图
使用代码
该示例包含两个独立的部分
- 一个简单的 Visual Basic 6 程序,演示了如何使用
WM_COPYDATA
将某些信息发送到 Media Center 插件。我选择 VB 6 的原因在于,本文的根源是 TapiRex,我开发的这款共享软件程序可以显示来电的来电显示,它也使用这种技术来在 Media Center UI 中显示来电显示和其他一些信息(有关详细信息,请参阅 www.cbuenger.com)。
- Media Center 2005 的后台插件,Media Center 在启动时会自动加载该插件,并监听传入的
WM_COPYDATA
消息,使用 Media Center API 函数显示包含的文本。这一部分必须使用 .NET Framework 1.0 进行编译,因为 Microsoft 的 Media Center 只接受 .NET 1.0 的插件。源代码包含一个简单的批处理文件,演示了如何在没有 Visual Studio IDE 的情况下编译该插件。
让我们深入了解细节:后台插件中的“技巧”是将整个插件不仅从 Microsoft 为插件建议的基类(IAddInModule
、IAddInEntryPoint
、IDisposable
)派生,还从 System.Windows.Forms.NativeWindow
派生。这为我们提供了一个窗口句柄,可以挂接并监听发送到此窗口的任何 Windows 消息,并过滤 WM_COPYDATA
。
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_COPYDATA)
{
...
}
base.WndProc(ref m);
}
但是,仅仅创建一个窗口句柄还不够:我们的 MessageSender 必须找到这个窗口。所以我们给这个不可见的窗口起一个非常独特的标题。
public mceDialogAddIn()
{
//Creating Window based on globally known name, and create handle
//so we can start listening to Window Messages.
CreateParams Params = new CreateParams();
Params.Caption = "<? .: mceDialog.MessageServer :. ?>";
this.CreateHandle(Params);
}
这样我们就可以在 MessageSender 中使用 FindWindow
找到这个窗口。
'Get TargetWindow handle
hwndTarget = FindWindow(vbNullString, "<? .: mceDialog.MessageServer :. ?>")
If (hwndTarget = 0) Then
' MCE addin-window not found
Exit Function
End If
VB 6 中的 MessageSender 有一个重要功能,就是使用 SendMessage
将消息发送到插件。
Dim uCopyData As COPYDATASTRUCT
Dim sData As String
Dim strDelim As String
strDelim = "¦"
' build string to send
' single parameters are seperated by "¦"
' the parameters:
' [0] = caption
' [1] = text to show
' [2] = image to display
' [3] = hide dialog automatically after these seconds
sData = strCaption & strDelim & strText & strDelim & _
strImage & strDelim & intAutoHide & strDelim
'Fill up the structure
With uCopyData
.cbData = LenB(sData)
.lpData = StrPtr(sData)
End With
Call SendMessage(hwndTarget, WM_COPYDATA, 0, uCopyData)
' hwndTarget is the window handle of the invisble
' Media Center addin window
为了简单起见,插件的数据仅用“¦”分隔。这样,我们在插件接收到数据时就可以进行分割。
if (m.Msg == WM_COPYDATA)
{
// arguments are delimited by "¦".
// the argument in order:
// [0] = caption
// [1] = text to show
// [2] = image to display
// [3] = hide dialog automatically after these seconds
COPYDATASTRUCT st = (COPYDATASTRUCT) Marshal.PtrToStructure(m.LParam,
typeof(COPYDATASTRUCT));
string strData = Marshal.PtrToStringUni(st.lpData);
string strDelim = "¦";
string[] args = strData.Split(strDelim.ToCharArray());
int intTimeOut = Int32.Parse(args[3]);
string strImage = args[2].Replace("\\", "\\\\");
if(!File.Exists(strImage))
// if image does not exist,
// do not pass to Dialog or it will not show up
strImage = "";
...
}
现在,数据就在我们想要的地方了:在 Media Center GUI 的进程中。因为这个插件引用了 Microsoft.MediaCenter
,所以我们可以从我们的插件中访问 Media Center API。但是,从插件的某个位置直接显示 MCE 风格的 MessageBox
并没有直接的方法…… Media Center API 中有一个类似于 MessageBox
函数的函数,那就是 HostControl.Dialog
(有关详细信息,请参阅 MSDN)。因此,我们需要一个 HostControl
的句柄。在 Media Center 启动并加载插件时调用的函数 void IAddInEntryPoint.Launch(AddInHost host)
中,会将 HostControl
的引用传递给插件。所以我们所要做的就是保存对 host
的这个引用,以便以后显示对话框。
void IAddInEntryPoint.Launch(AddInHost host)
{
mhcControl = host.HostControl;
...
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_COPYDATA)
{
...
Object[] oButtons = new Object[1];
oButtons[0] = 1; // just an OK button
mhcControl.Dialog(args[1], args[0], oButtons, intTimeOut,
false, strImage, null);
// mhcControl is our saved reference to a HostControl
}
base.WndProc(ref m);
}
关注点
有一件非常重要的事情需要知道:后台插件会在执行离开 IAddInEntryPoint.Launch
函数时被 Media Center 卸载。所以,如果您希望插件停留在内存中(例如,等待特定消息,如本例),您必须在 Launch
函数中“保持”执行,只有当您想卸载它时,才将其交还给 Media Center 进程。
void IAddInEntryPoint.Launch(AddInHost host)
{
...
while (true)
{
// Yield processing till we sample for inactivity
// if we let the execution exit "Launch",
// the addin will be unloaded
// and is then unable to get messages...
Thread.Sleep(100);
System.Windows.Forms.Application.DoEvents();
}
}
调用 DoEvents
只是为了让线程有机会处理 WndProc
函数,以过滤传入的消息。
更多
本示例仅展示了从外部应用程序到后台插件的单向“通信”。如果您希望让您的应用程序知道用户在对话框中点击了哪个按钮,您需要建立一个从插件到您的应用程序的连接。这可以通过相同的方式(使用 WM_COPYDATA
)或您喜欢的任何其他方式来完成。