托管托管代码和垃圾回收
Hosting managed code and garbage collection
我有一个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
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;
}
}
}
- 如何在 c++ 非托管代码中反序列化 byte[] 的 json 字符串?
- 如何使用指针直接从托管代码中的非托管代码中读取矢量数据
- 异步接口托管 - > 非托管代码
- 从C++(非托管代码)检索数组到 C 尖锐形式(托管)
- 从本机代码返回到托管代码会损坏返回的对象
- 将C++非托管代码转换为 C#
- 将托管事件处理程序传递给 Linux 中的非托管代码
- 访问在托管代码创建的事件上对WaitforsingLeoBject拒绝
- 使用 P/Invoke 时存储非托管代码的数据
- Windows 服务导入C++非托管代码
- 将字符串数组从托管代码编组到本机代码
- P/从获取"tried to access protected memory error"调用非托管C++代码
- 将数据从非托管代码传递到托管代码
- 为使用C# /托管代码的无注册COM Interop设置DLL
- 从无操纵项目(C/C VS2005)调用托管代码(C#/Visual Basic VS2010)
- 从 VB.NET 调用非托管代码C++ dll
- 当C#中的字节数组传递给结构中的非托管代码时,会发生什么情况
- 在托管代码和非托管代码之间传递非托管结构的安全数组
- 如何调试C++非托管代码中的较低级别文件访问异常/崩溃
- 自定义Direct2D呈现托管代码和非托管代码之间的批封送处理