托管托管代码和垃圾回收

Hosting managed code and garbage collection

本文关键字:托管代码      更新时间:2023-10-16

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

出于各种原因,我正在考虑消除解决方案的C++部分。但是,由于我无法控制的约束,我必须保留进程外 COM 服务器。Microsoft在这里确实有一个规范的例子。

看着这个例子,我有些不明白。在消息循环开始之前,将创建一个计时器来调用 GC。每 5 秒收集一次。我能找到的唯一提及表明这是为了确保在合理的时间范围内发布 COM 对象。我对此有点困惑...我的C++主机目前是否调用 GC。自动收集?我当然不会这样做。然而,我正在创建托管对象(使用 COMVisible(true) 作为 COM 对象在C++代码中。这是否意味着我应该打电话给GC。现在每 5 秒收集一次?如果不是,为什么我需要在这个新的 C# 进程外服务器中调用它。这是为了弥补在正常C++应用程序中清理未引用的 COM 对象的自动过程吗?(我假设这在消息循环期间的某个时间发生。

调用 GC。每 5 秒收集一次似乎是一个坏主意。我担心错了吗?有没有其他方法可以达到相同的结果?

我正在使用.NET 4.5和Visual Studio 2012。

IMO,在 C# 中创建 COM 进程外服务器的最简单方法是使用 DLL 代理进程。

您仍然仅限于双接口 ( ComInterfaceType.InterfaceIsDual ),并且您需要注册生成的类型库(并作为部署的一部分执行此操作):

C:WindowsMicrosoft.NETFrameworkv4.0.30319RegAsm.exe ManagedServer.dll /codebase /tlb

这将允许你使用 COM 类型库封送器,因为你没有用于 C# COM 对象的专用 COM 代理/stuf DLL。

确保使用正确的RegAsm.exe二进制文件,具体取决于ManagedServer.dll程序集的目标位数。以上假定为 x86 代码。

下面是一个完整的工作模板示例。它负责代理注册:

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 单元中创建,因此可以在任何线程上调用接口方法,您需要实现线程安全。

如果您需要一个在对象的代理进程内带有消息泵的 STA 线程,您可以通过实现工厂单例并使用 CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream 将对象导出到 STA 线程之外(这可能是相关的)来显式执行此操作。

另一点,您的 COM 客户端代码在创建此实例时应使用 CLSCTX_LOCAL_SERVER ManagedComObjectActivator.CreateInstance(Type.GetTypeFromProgID("Noseratio.ManagedComObject")) 显然使用 CLSCTX_ALL ,情况并非如此。这可以很容易地处理:

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;
        }
    }
}