Add COM and ActiveX Support in .NET Compact Framework Using Odyssey Software CFCOM(中文)
Andy Sjöström
businessanyplace.net
September 2003
Applies to:
Microsoft® .NET Compact Framework 1.0
Microsoft Visual Studio® .NET 2003
Odyssey Software CFCOM 1.0
Microsoft Windows Mobile™–based Pocket PC
aawolf的话:本月给大家带来的是一篇上了MSDN首页的文章,读者评价为7。原文发于2003年9月8日,而且话题也是大家比较关心的在CF下如何调用COM和ActiveX组件。所以,老狼抽出一个下午的时间翻译过来,希望的是大家能了解目前最新的技术,并与之保持同步。但老狼毕竟是个程序员而不是专业译者,水平粗糙不说,也没有很多时间翻译文章,所以还是希望大家能多读写英文资料,老狼的翻译仅做参考。原文的连接如下:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnppc2k3/html/cfcom.asp
摘要:学习在Visual C#中如果将COM和ActiveX支持添加到.NET Compact Framework应用程序中来。还有,如何在.NET Compact Framework应用程序中如何使用Macromedia Flash Player 6 for a Microsoft Windows Mobile–based Pocket PC。
背景:COM和ActiveX的互用性在full .NET Framework中是可用的。受管.NET Compact Framework代码可以使用Platform Invoke (P/Invoke)调用DLL中的非受管代码,但是在.NET Compact Framework中对COM和ActiveX的支持是不可用的。Odyssey Software的 CFCOM作为介入层将COM对象和ActiveX控件作为.NET equivalences暴露给.NET Compact Framework应用程序,使用一个大约只有30K的memory foot print。
Odyssey Software CFCOM能够透明地访问控件(比如Windows Media Player)和对象(比如Pocket Outlook Object Model 和 ADOCE),和实际上任何第三方的COM或者ActiveX组件。CFCOM可以通过Odyssey Software得到许可,尽管文章中的代码示例可以通过license key被创建在代码中。
Download cfcom.exe from the Microsoft Download Center.
Contents
Macromedia Flash 6 Player for Pocket PC
Flash Anyplace Code Walk-through
Introduction
姑且不论大小,.NET Compact Framework的命名空间和类的结构层次和完整的.NET framework是十分相似的。当然,因为体积和执行效率的问题,去掉了.NET Framework的一些特性。其中之一就是COM互用性。Microsoft® Windows® CE和Pocket PC的许多核心模块都是作为COM对象和ActiveX控件实现的,比如连通性、消息、通讯、图形、媒体、对象存储和个人信息管理(PIM)。也有许多第三方COM和ActiveX控件被用在移动设备上,例如GPS、图表、安全和连通性。Platform Invoke (P/Invoke)存在于System.Runtime.InteropServices命名空间中,可以用来调用简单的非受管代码函数。第三方有效提供者的一个实例是Sapphire Solutions Ltd (http://www.sapphire-solutions.co.uk/),使用存在的.NET Compact Framework服务是足够的。Sapphire Solutions通过组件和源代码远程访问服务、编密码等等。Sapphire Solutions组件是通过Platform Invoke加载到.NET Compact Framework代码示例中的,使之更早地开始使用这些代码。
调用更复杂的非受管代码函数需要需要一个大量的复杂编码,包括更多有挑战性的领域,比如marshalling。开发COM封装往往是需要单一的基于函数的接口。使COM和ActiveX融合到受管代码中是充满挑战的。
Odyssey Software CFCOM
Odyssey Software对于Windows CE开发者社区来说并不陌生。他们从1996年开始就已经在推动企业级移动应用程序开发方面扮演重要的角色了,他们的产品有CEfusion 和 ViaXML。公司连续为移动应用开发社区提供工具,这次他们带来的是一个介于COM和Compact Framework中间的转换层,可以使COM暴露等价于.NET的接口。CFCOM为COM和ActiveX转换到Compact Framework应用程序提供广泛的互用性。
CFCOM可以被看作一个使COM和.NET对象能够运行在同一个应用程序中的封装。一个Compact Framework对象被Common Language Runtime (CLR)约束在受管内存中,当一个COM对象保存在非受管内存中。CFCOM实现了一个透明的转换层,使受管和非受管组件可以相互作用:设置或取得属性值,调用方法,捕捉COM事件,处理高级类型转换函数(a k a marshalling)并从非受管代码的异常事件中产生本地.NET异常。
对于开发者来说,CFCOM由三个文件组成:
· Odyssey.CFCOM.dll包含CFCOM运行时。
· Odyssey.CFCOM.Design.dll 提供Visual Studio的设计期支持。该文件被安装在C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\ v1.0.5000\Windows CE\Designer目录中。
· Odyssey.CFCOM.1248.dll是本地(设备支持)运行时组件。
图一显示ComObject和ComControl工具被添加到toolbox中。
Figure 1. ComObject and ComControl tools added to the toolbox
Odyssey Software也包括一个示例Windows Media Player Wrapper Designer,开发者可以添加到toolbox中并在设计期设置属性值。使用Windows Media Player 的Compact Framework应用程序可以被快速创建。
对于eMbedded Visual Basic开发者来说,CFCOM意味着他们能够使用熟悉的COM对象和ActiveX组件,而不需要学习C/C++和P/Invoke。eMbedded Visual C++开发者可以继续使用C++来写他们应用程序的性能临界(performance-critical)部分,而不用创建本地封装并使用custom marshalling处理
Odyssey Software CFCOM的更多信息,您可以读Microsoft Case Study, COM Interop Library Gives Compact Framework Applications Easy Access to COM Components and ActiveX Controls.
Code Walk-through
CFCOM的设计思想就是近可能实现透明转换。在使用ActvieX控件或COM对象前只需要做两件事。
· ActiveX控件或者COM对象的ProgID 或者 CLSID。
· 从Odyssey Software获得的CFCOM 的license key。
在这之后,可以开始使用CFCOM的调用方法来使用组件了。下面的代码显示如何在一个Compact Framework应用程序中使用Windows Media Player。
License key在应用程序的主函数入口处被设置。
ComObject.AddLicense(CFCOM_LICENSEKEY);
单窗体(one-form)应用程序可以开始使用Windows Media Player。
public class frmMain : System.Windows.Forms.Form
{
private const string CFCOM_LICENSEKEY = " XXXXXXXXXXXXXXXXXXXXXXXXXX ";
public frmMain()
{
InitializeComponent();
mpc.ProgID = "WMPCEOCX.WMP";
mpc.Invoke("ShowControls", InvokeFlags.SetProperty, false);
mpc.Invoke("ShowAudioControls", InvokeFlags.SetProperty, false);
mpc.Invoke("ShowPositionControls", InvokeFlags.SetProperty, false);
mpc.Invoke("ShowTracker", InvokeFlags.SetProperty, false);
mpc.Invoke("ShowStatusBar", InvokeFlags.SetProperty, true);
mpc.Invoke("Autostart", InvokeFlags.SetProperty, true);
mpc.Invoke("Filename", InvokeFlags.SetProperty, "http://news.com.com/1604-2-966406-1.asx?msft_awe+win");
mpc.Invoke("SendMouseClickEvents", InvokeFlags.SetProperty, true);
mpc.ComObject.ComEvent += new ComEventHandler(mpc_COMEvent);
}
请注意如何设置ProgID,然后如何通过Invoke方法设置属性。InvokeFlags enumeration包括SetProperty, GetProperty, 和 CallMethod。
更多关于这些代码和如何在Compact Framework应用程序中添加多媒体的信息请看,"Play Media with .NET CF using CFCOM" http://www.businessanyplace.net/?p=cfcommedia.
图二显示使用Windows Media Player ActiveX的应用程序。
Figure 2. Windows Media Player Active X control
Macromedia Flash 6 Player for Pocket PC
Macromedia已经退出了播放Macromedia Flash内容的免费Pocket PC播放器。Flash是一个流行的平台,用来发布high profile content, movies,甚至可以用于移动应用程序。Flash开发团体是活跃的并在增长。基于Visual Studio .NET and the Compact Framework,开发者在移动应用程序使用Flash 容易得创造出奇异的内容和应用程序。Pocket PC player的个人免费版本可以在Macromedia Web服务器上找到:http://www.macromedia.com/software/flashplayer/pocketpc/2002.html.
该播放器是一个ActiveX控件,大部分是用在Pocket Internet Explorer中,物理上作为flash.dll被安装在\Windows\Macromedia目录中。一旦播放器被安装,用户可以通过Pocket Internet Explorer观看Macromedia Flash 内容。一种做法是将一个Flash内容文件(.swf)和HTML文件放在同一个目录下,并打开HTML文件。该HTML文件显示ActiveX控件如何被调用。下列片段来自HTML文件:
<HTML>
<HEAD>
<TITLE>book</TITLE>
</HEAD>
<BODY bgcolor="#53695C">
<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://active.macromedia.com/flash2/cabs/swflash.cab#version=6,0,0,0"
ID=rapier4 WIDTH=230 HEIGHT=255>
<PARAM NAME=movie VALUE="book.swf">
<PARAM NAME=menu VALUE=false>
<PARAM NAME=quality VALUE=high>
<PARAM NAME=bgcolor VALUE=#53695C>
</OBJECT>
</BODY>
</HTML>
在"PARAM"属性后,ActiveX控件被使用参数引用调用。叫做“book.swf”的电影被播放,Flash内容菜单被关闭,播放质量为高,背景色被设为#53695C。
图三显示Pocket Internet Explorer中的效果。
Figure 3. Pocket PC Flash player in action
这个例子中的Macromedia Flash content由来自flashenabled.com (http://www.flashenabled.com/)的Flash开发者Phillip Torrone设计。更多相似的例子和关于Pocket PC 和Macromedia Flash相关信息可以查看Pocket PC Flash (http://www.pocketpcflash.net/home.htm).
更多关于Falsh播放器参数的信息,请看"Scripting with Flash Player and ActiveX" 来自Macromedia Web 服务器: http://www.macromedia.com/support/flash/ts/documents/activex_script.htm.
下面让我们来看如何在一个Compact Framework应用程序中使用Flash player.
Flash Anyplace
Flash Anyplace是一个使用CFCOM使用Flash ActiveX控件的应用程序。Flash Anyplace可以播放内容并使用户控制如何播放内容。图4显示第一个窗体。
Figure 4. Flash player settings
在这个窗体中,用户可以设置播放哪个Flash文件,并控制Flash 文件播放的行为。第二个窗体(图5)显示实际的Flash文件被播放。
Figure 5. Viewing the file
Flash Anyplace Code Walk-through
这个工程包含两个窗体。当用户在第一个窗体中点击“Play”按钮时下列代码被执行:
private void btnPlay_Click(object sender, System.EventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
frmFlashAnyplacePlayer flashAnyplacePlayer = new frmFlashAnyplacePlayer(this, this.txtFlashFile.Text, this.cmbQuality.SelectedIndex, this.cmbScale.SelectedIndex, this.chkLoop.Checked, this.chkMenu.Checked, this.txtBackgroundColor.Text);
flashAnyplacePlayer.ShowDialog();
}
参数被传递给第二个窗体,一个CFCOM ComControl在这个窗体中。Macromedia Flash Player的ProgID是ShockwaveFlash.ShockwaveFlash。下面是这个窗体的初始化InitializeComponent部分:
this.flashPlayer = new Odyssey.CFCOM.ComControl();
this.flashPlayer.ProgID = "ShockwaveFlash.ShockwaveFlash";
像在Pocket Internet Explorer中播放Flash内容的HTML文件中那样使用CLSID创建也是可以的。这行将这么写:
this.flashPlayer.ProgID = "{D27CDB6E-AE6D-11cf-96B8-444553540000}";
注意license key在ComControl起作用前已经被设置。首先,在声明中设置license key字符串。
private const string CFCOM_LICENSEKEY = "XUHXRATLAUVKL96EFVBH9BEUV7";
这个license key是CFCOM只能使用Flash player(使用ProgID)并只能用在这个工程中。AddLicense方法应该被优先放置在Application.Run中启动窗体之前的位置。CFCOM将在每一个过程中查找这个许可。
static void Main()
{
// Set license key
ComControl.AddLicense(CFCOM_LICENSEKEY);
Application.Run(new frmFlashAnyplace());
}
如果Flash文件在Internet上,使用URL作为电影地址。例如http://www.businessanyplace.net/files/FlashAnyplace.swf.如果Flash文件定位在Pocket PC上,使用下面的格式:
file://\My%20Documents\mymovie.swf
Flash Anyplace工程将一个Flash文件作为一个Content item,被安装在应用程序目录中。得到当前应用程序目录并将这个Flash文件作为默认值,下面的代码会执行:
// Get application directory
string currentDir = new FileInfo(Assembly.GetExecutingAssembly().GetName().CodeBase).DirectoryName;
// Set default values
txtFlashFile.Text = @"file://" + currentDir + @"\book.swf";
第二个窗体的load事件通过CFCOM得到传递给Flash player的参数:
try
{
flashPlayer.Invoke("quality", InvokeFlags.SetProperty, quality);
flashPlayer.Invoke("bgcolor", InvokeFlags.SetProperty, backgroundColor);
flashPlayer.Invoke("scale", InvokeFlags.SetProperty, scale);
flashPlayer.Invoke("loop", InvokeFlags.SetProperty, loop);
flashPlayer.Invoke("menu", InvokeFlags.SetProperty, menu);
flashPlayer.Invoke("movie", InvokeFlags.SetProperty, flashFile);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
parentForm.Hide();
Cursor.Current = Cursors.Default;
}
如果我们尝试将一个无效的参数名传递给Macromedia Flash Player,异常处理将返回一个适当的异常。例如,当传递“movie1”作为一个属性替代“movie”,CFCOM将抛出一个异常"0x80020006: DISP_E_UNKNOWNNAME."
最后,当用户离开这个带有Flash player的窗体,CFCOM ComControl需要被释放:
private void frmFlashAnyplacePlayer_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
flashPlayer.Dispose();
parentForm.Show();
}
来自CFCOM的帮助文件:“也许像CFCOM一样简单,有一个非常重要的问题需要被了解,即垃圾回收的不确定性,经常手工回收COM资源是必须的。ComObject.Dispose 和 ComControl.Dispose方法是成熟的。某些ActiveX控件,比如Windows Media Player,当他们在被垃圾回收释放之前会出现一些未知的行为。”
ConclusionOdyssey Software CFCOM致力于Compact Framework开发中一些非常有挑战性的领域:ActiveX和COM互用性。通过执行一个受管代码和非受管代码之间的转换层,开发者可以致力于向核心挑战,从而给他们的客户带来正常的价值。