托管托管代码和垃圾收集 [英] Hosting managed code and garbage collection

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

问题描述

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

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 服务器.Microsoft 确实在这里提供了一个典型示例.

For a variety of reasons, I am considering eliminating the C++ portion of my solution. However, because of constraints outside of my control I have to retain the out-of-process COM server. Microsoft does have a canonical example of this here.

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

Looking at this example there is something I don't understand. Before the message loop starts, a timer is created to call GC.Collect every 5 seconds. The only mention of this that I can find indicates it's to ensure the COM objects are released in a reasonable timeframe. I'm a little confused about this...does my C++ host currently call GC.Collect automatically? I'm certainly not doing it. And yet I am creating managed objects (with COMVisible(true) as COM objects in the C++ code. Does that mean I should be calling GC.Collect every 5 seconds now? If not, why do I need to call it in this new C# out of process server. Is that to make up for the automatic process that cleans up unreferenced COM objects in a normal C++ application? (Which I assume is happening sometime during the message loop.)

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

Calling GC.Collect every 5 seconds seems like it could be a bad idea. Am I wrong to worry? Is there some other method by which I could achieve the same results?

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

I am using .NET 4.5 and Visual Studio 2012.

推荐答案

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

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):

C:WindowsMicrosoft.NETFrameworkv4.0.30319RegAsm.exe ManagedServer.dll/codebase/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 客户端代码在创建 ManagedComObject 的实例时应该使用 CLSCTX_LOCAL_SERVER.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;
        }
    }
}

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆