本文介绍了主机托管代码和垃圾回收的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个C ++托管了大量的C#代码来支持由C ++ COM对象公开的API外的进程COM服务器。

I have a C++ out-of-process COM server that hosts a lot of C# code to support the API exposed by the C++ COM objects.

由于各种原因,我正在考虑消除我的解决方案的C ++部分。但是,因为我的控制范围之外的限制我的有无的保留进程外的COM服务器。微软确实有这里此。

IMO, the easiest way to create a COM out-of-proc server in C# is to use a DLL surrogate process.

您还在局限于双接口( ComInterfaceType.InterfaceIsDual ),然后你需要注册生成的类型库(并做到这一点作为部署的一部分,太):

You're still limited to dual interfaces (ComInterfaceType.InterfaceIsDual), and you'd need to register the generated type library (and do it as part of deployment, too):

ç :\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe ManagedServer.dll /代码库/ TLB

这将允许您利用COM类型库编组,因为你没有为你的C#的COM对象专用的COM代理/ STUF DLL。

This will allow you to utilize the COM type library marshaller, because you don't have a dedicated COM proxy/stuf DLL for your C# COM objects.

请务必使用正确的 RegAsm.exe 二进制文件,这取决于你的 ManagedServer.dll 装配的目标位岬。以上假设x86代码。

Make sure to use the correct RegAsm.exe binary, depending on the target bit-ness of your ManagedServer.dll assembly. The above assumes x86 code.

下面是一个完整的工作模板的例子。它采用代理注册护理:

Here is a complete working template example. It takes care of the surrogate registration:

using Microsoft.Win32;
using System;
using System.Runtime.InteropServices;

namespace ManagedServer
{
    [ComVisible(true), Guid("1891CF89-1282-4CA8-B7C5-F2608AF1E2F1")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IManagedComObject
    {
        string ComMethod(string data);
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComDefaultInterface(typeof(IManagedComObject))]
    [Guid("989162CD-A6A6-4A7D-A7FB-C94086A4E90A")]
    [ProgId("Noseratio.ManagedComObject")]

    public class ManagedComObject : IManagedComObject
    {
        // public constructor
        public ManagedComObject()
        {
        }

        // IManagedComObject
        public string ComMethod(string data)
        {
            return data;
        }

        // registration
        [ComRegisterFunction()]
        public static void Register(Type type)
        {
            var guid = type.GUID.ToString("B");
            using (var appIdKey = Registry.ClassesRoot.CreateSubKey(@"AppID\" + guid))
            {
                appIdKey.SetValue("DllSurrogate", String.Empty);
            }
            using (var appIdKey = Registry.ClassesRoot.CreateSubKey(@"CLSID\" + guid))
            {
                appIdKey.SetValue("AppId", guid);
            }
        }

        [ComUnregisterFunction()]
        public static void Unregister(Type type)
        {
            var guid = type.GUID.ToString("B");
            using (var appIdKey = Registry.ClassesRoot.OpenSubKey(@"AppID\" + guid, writable: true))
            {
                if (appIdKey != null)
                    appIdKey.DeleteValue("DllSurrogate", throwOnMissingValue: false);
            }
            Registry.ClassesRoot.DeleteSubKeyTree(@"CLSID\" + guid, throwOnMissingSubKey: false);
        }
    }
}

默认情况下,对象将在MTA的公寓中创建的,所以接口方法有可能在任何线程调用,你需要实现线程安全。

By default, the objects will be created in MTA apartment, so the interface methods may possibly be called on any thread, you'd need to implement thread safety.

如果你需要为你的对象的替代进程内消息泵STA线程,你可以做到这一点明确实施工厂单,并使用 CoMarshalInterThreadInterfaceInStream对该 / CoGetInterfaceAndReleaseStream对导出STA线程之外的对象(的可能是相关的)。

If you need an STA thread with message pump inside the surrogate process for your objects, you could do that explicitly by implementing a factory singleton and using CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream to export objects outside the STA thread (this might be related).

还有一点,你的COM客户端代码应该使用 CLSCTX_LOCAL_SERVER 时创建 ManagedComObject 这样的一个实例。这是不是与 Activator.CreateInstance的情况下(Type.GetTypeFromProgID(Noseratio.ManagedComObject)),这显然使用了 CLSCTX_ALL 。这可以很容易地照顾:

Another point, your COM client code should use CLSCTX_LOCAL_SERVER when creating this an instance of ManagedComObject. This is not the case with Activator.CreateInstance(Type.GetTypeFromProgID("Noseratio.ManagedComObject")), which apparently uses CLSCTX_ALL. This can be easily taken care of:

using System;
using System.Runtime.InteropServices;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            // dynamic obj = Activator.CreateInstance(Type.GetTypeFromProgID("Noseratio.ManagedComObject"));

            dynamic obj = ComExt.CreateInstance(
                Type.GetTypeFromProgID("Noseratio.ManagedComObject").GUID,
                localServer: true);

            Console.WriteLine(obj.ComMethod("hello"));
        }
    }

    // COM interop
    public static class ComExt
    {
        const uint CLSCTX_LOCAL_SERVER = 0x4;
        const uint CLSCTX_INPROC_SERVER = 0x1;

        static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");

        [DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)]
        static extern void CoCreateInstance(
           [MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
           [MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter,
           uint dwClsContext,
           [MarshalAs(UnmanagedType.LPStruct)] Guid riid,
           [MarshalAs(UnmanagedType.Interface)] out object rReturnedComObject);

        public static object CreateInstance(Guid clsid, bool localServer)
        {
            object unk;
            CoCreateInstance(clsid, null, localServer ? CLSCTX_LOCAL_SERVER : CLSCTX_INPROC_SERVER, IID_IUnknown, out unk);
            return unk;
        }
    }
}

这篇关于主机托管代码和垃圾回收的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-22 21:02
查看更多