c # — хостинг управляемого кода и сборка мусора

У меня есть C ++ внепроцессный COM-сервер, на котором размещено много кода C # для поддержки API, предоставляемого C ++ COM-объектами.

По разным причинам я рассматриваю возможность исключения части моего решения на C ++. Однако из-за ограничений вне моего контроля я иметь сохранить внепроцессный COM-сервер. У Microsoft есть канонический пример этого Вот.

Глядя на этот пример, я кое-что не понимаю. Перед началом цикла сообщений создается таймер для вызова GC.Collect каждые 5 секунд. Единственное упоминание об этом, которое я могу найти, указывает на то, что это гарантирует, что COM-объекты будут выпущены в разумные сроки. Я немного запутался по этому поводу … мой C ++ хост в настоящее время вызывает GC.Collect автоматически? Я конечно не делаю этого. И все же я создаю управляемые объекты (с COMVisible (true) как COM-объекты в коде C ++. Означает ли это, что я должен вызывать GC.Collect сейчас каждые 5 секунд? Если нет, зачем мне вызывать его в этом новом C #) сервер вне процесса. Это для того, чтобы компенсировать автоматический процесс, который очищает COM-объекты, на которые нет ссылок, в обычном приложении C ++ (что, как я полагаю, происходит во время цикла сообщений).

Вызов GC.Collect каждые 5 секунд кажется плохой идеей. Я ошибаюсь, чтобы беспокоиться? Есть ли какой-то другой метод, с помощью которого я мог бы достичь таких же результатов?

Я использую .NET 4.5 и Visual Studio 2012.

3

Решение

IMO, самый простой способ создать COM-сервер в C # — использовать DLL суррогатный процесс.

Вы по-прежнему ограничены двойными интерфейсами (ComInterfaceType.InterfaceIsDual), и вам необходимо зарегистрировать сгенерированную библиотеку типов (и сделать это как часть развертывания):

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

Это позволит вам использовать маршаллер библиотеки типов COM, потому что у вас нет выделенной библиотеки DLL COM-прокси / Stuf для ваших объектов C # COM.

Убедитесь, что вы используете правильный 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 при создании этого экземпляра ManagedComObject, Это не так с Activator.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;
}
}
}
2

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector