托管托管代码和垃圾回收

我有一个C ++进程外COM服务器,它承载了很多C#代码,以支持C ++ COM对象公开的API。

由于各种原因,我正在考虑取消我的解决方案的C ++部分。 但是,由于我无法控制的限制,我必须保留进程外的COM服务器。 微软确实在这里有一个典型的例子。

看看这个例子,我有些不明白。 在消息循环开始之前,会创建一个计时器,每隔5秒调用GC.Collect。 我能找到的唯一提到的就是确保在合理的时间范围内释放COM对象。 我对此有点困惑…我的C ++主机当前是否自动调用GC.Collect? 我当然不是这样做的。 然而,我正在创建托管对象(COMVisible(true)作为C ++代码中的COM对象。这是否意味着我现在应该每5秒调用一次GC.Collect?如果没有,为什么我需要在这个新的C#中调用它是否可以弥补在普通C ++应用程序中清理未引用的COM对象的自动进程?(我假设在消息循环期间的某个时间发生了这种情况)。

每5秒调用一次GC.Collect似乎可能是一个坏主意。 我担心吗? 是否有其他方法可以达到相同的效果?

我使用的是.NET 4.5和Visual Studio 2012。

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

您仍然只能使用双接口( ComInterfaceType.InterfaceIsDual ),并且您需要注册生成的类型库(并将其作为部署的一部分):

C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe ManagedServer.dll /codebase /tlb

这将允许您使用COM类型库marshaller,因为您没有为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线程之外的对象( 这可能是相关的)来显式地执行此操作 。

另一点,在创建ManagedComObject实例时,COM客户端代码应该使用CLSCTX_LOCAL_SERVERActivator.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; } } }