将托管控件当作ActiveX 控件使用

 

发布时间: 星期一,11/24/2008,  11:31PM

        作者: andreww

原稿地址: Using Managed Controls as ActiveX Controls

 

在不用VSTO 的情况下,你能够 在办公文档中像 使用本地的ActiveX 控件一样使用托管的用户控件 吗? 不久以前,我写了一篇介绍在文档级别的VSTO 解决方案中使用本地的ActiveX 的控件的文章,方法是把ActiveX 控件封装在托管的用户控件中。一个名叫Casey 的读者问到:“能不能通过另外的方法达到此目的呢?”。 答案是可能的,更准确地说,“我不能100% 保证你一定可以,因为很有可能技术上不支持, 或者很可能在大多数情况不可用。 如果你仍然想试一试,请继续向下看。

 

第一,创建一个托管用户控件的工程(译者注: windows vista 上请用administrator 权限 打开 VSTO ),即一个窗口表单类库或者控件类库工程。使用用户控件设计器 (IDE)来设计你的用户控件(你可以使用任何标准控件在该工程中。)

 

第二,在工程属性里, 找到Build table标签(编译选项卡) “Register for COM interop”( 中文译为: “ COM Interop 注册组件”, Register for COM interop 项目属性指定托管应用程序是否将公开一个允许 COM 对象与托管应用程序进行交互的 COM 对象(COM 可调用的包装)) 复选框前打钩 这个步骤将会注册你工程中任何com 可见的类,同时会生成一个COM 类库并且自动注册该库。

 

第三, 添加属性使你的用户控件类为com 可见的(即能够被 com 发现或访问), 同样, 指定一个GUID 这样能够避免在每次重新编译的时候得到一个新的不同的GUID 指定编译器为你的控件生成一个自动派发类接口。

[ComVisible(true)]

[Guid("0F2A2E9D-C79E-4b1b-9AF7-2D1487F29041")]  

[ClassInterface(ClassInterfaceType.AutoDispatch)]

public partial class ManagedOcx : UserControl

{

 

下一步, 我们需要提供一些额外的注册表键值: Control MiscStatusTypeLib, Version。 你可以通过使用.Reg 脚本来完成此操作,但是下面的方式更好:你可以写一些函数使它在注册和注销的时候都能够被调用(通过 加上属性 ComRegisterFunction/ComUnregisterFunction 来使用此功能)来完成此功能

Control 是一个空的子键。 TypeLib 这个键将映射到应用程序(或者类库)TypeLib GUID (这是一个程序集级别的GUID,在 assemblyinfo.cs中有对应的项)。Version 的键值来自程序集版本号中的主版本号和次版本号。稍微有点意思的是子键MiscStatus,它的值由枚举OLEMISC 中一些枚举值组合后的位运算得到的, 可以参考文档  OLEMISC Enumeration . 为了使用该枚举,你需要添加对Microsoft.VisualStudio.OLE.Interopd.dll的引用同时在代码文件中开头添加:(using  Microsoft.VisualStudio.OLE.Interop;)引用.

 [ComRegisterFunction]

static void ComRegister(Type t)

{

    string keyName = @"CLSID\" + t.GUID.ToString("B");

    using (RegistryKey key =

        Registry.ClassesRoot.OpenSubKey(keyName, true))

    {

        key.CreateSubKey("Control").Close();

        using (RegistryKey subkey = key.CreateSubKey("MiscStatus"))

        {

            // 131456 decimal == 0x20180.

            long val = (long)

                ( OLEMISC.OLEMISC_INSIDEOUT

                | OLEMISC.OLEMISC_ACTIVATEWHENVISIBLE

                | OLEMISC.OLEMISC_SETCLIENTSITEFIRST);

            subkey.SetValue("", val);

        }

        using (RegistryKey subkey = key.CreateSubKey("TypeLib"))

        {

            Guid libid =

                Marshal.GetTypeLibGuidForAssembly(t.Assembly);

            subkey.SetValue("", libid.ToString("B"));

        }

        using (RegistryKey subkey = key.CreateSubKey("Version"))

        {

            Version ver = t.Assembly.GetName().Version;

            string version =

              string.Format("{0}.{1}", ver.Major, ver.Minor);

            subkey.SetValue("", version);

        }

    }

}

 

[ComUnregisterFunction]

static void ComUnregister(Type t)

{

    // Delete the entire CLSID\{clsid} subtree for this component.

    string keyName = @"CLSID\" + t.GUID.ToString("B");

    Registry.ClassesRoot.DeleteSubKeyTree(keyName);

}

生成工程后,启动Excel 从开发者选项卡, 到控件组栏, 然后双击 插入一个按钮, 这个级联菜单打开当前可用的控件的一个列表。点击右下角的图标将弹出一个显示更多控件的对话框, 这将展示一个更多当前可用已注册的ActiveX 控件。 在这里你能够发现自己刚刚创建的控件.

注意:这在excel 运行是没问题的(我只做了很小一部分测试,并且成功了)。 部分的能够用于power point 但是在word 里全部的尝试都失败了。也许使用更多的OLEMISC值能够改变这种状况,或者有可能possibly there are some messages we need to hook;还有可能是我们需要实现更多的相关接口,但我没有继续去尝试。当然,VSTO 运行时有一套很好的宿主控件能够实现Excel Word  的这个功能, 但是一旦离开了VSTO 解决方案这个大环境, 他们就不可用了。这是一个有趣的尝试,但是它仍然不被支持。事实上, 我只能以有限的方式,使它在特殊的环境下才能够工作.但应该告诉你,在任何真正的场合,这绝对不是你想用的一个技术

posted @ 2008-12-26 13:34  dp/dt  阅读(520)  评论(0)    收藏  举报