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

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

问题描述

我有一个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服务器。微软确实有这里此的一个典型的例子。

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.

在这个例子中寻找有件事我不明白。消息循环开始之前,创建一个定时器来调用GC.Collect每5秒。这是我能找到的唯一一提表示它保证了COM对象在合理的时间内释放。我有点困惑这个...没有我的C ++托管当前自动调用GC.Collect?我当然不这样做。其实我作为C ++代码COM对象的创建管理对象(有标记有ComVisible特性(真)。这是否意味着我应该叫GC.Collect的每5秒了吗?如果没有,为什么我需要调用它在这个新的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.)

所以GC.Collect调用每5秒钟好像它可能是一个坏主意。我错了而烦恼?是否存在被,我可以达到同样的效果其他方法?

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,最简单的方法来创建一个COM外的进程内在C#服务器是使用 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):

ç :\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;
        }
    }
}

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

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