Hébergement de code géré et de récupération de place

J’ai un serveur COM C ++ hors processus qui héberge beaucoup de code C # pour prendre en charge l’API exposée par les objects COM C ++.

Pour diverses raisons, j’envisage d’éliminer la partie C ++ de ma solution. Cependant, en raison de contraintes indépendantes de ma volonté, je dois conserver le serveur COM hors processus. Microsoft en a un exemple canonique ici .

En regardant cet exemple, il y a quelque chose que je ne comprends pas. Avant le début de la boucle de messages, un temporisateur est créé pour appeler GC.Collect toutes les 5 secondes. La seule mention que je puisse trouver à ce propos indique que c’est pour s’assurer que les objects COM sont libérés dans un délai raisonnable. Je suis un peu confus à ce sujet … mon hôte C ++ appelle-t-il automatiquement GC.Collect automatiquement? Je ne le fais certainement pas. Et pourtant, je crée des objects gérés (avec COMVisible (true) en tant qu’objects COM dans le code C ++. Cela signifie-t-il que je devrais appeler GC.Collect toutes les 5 secondes maintenant? Sinon, pourquoi dois-je l’appeler dans ce nouveau C #? serveur hors processus. Est-ce cela pour compenser le processus automatique qui nettoie les objects COM non référencés dans une application C ++ normale (ce qui, je suppose, se produit parfois au cours de la boucle de messages)?

Appeler GC.Collect toutes les 5 secondes semble être une mauvaise idée. Ai-je tort de m’inquiéter? Existe-t-il une autre méthode permettant d’obtenir les mêmes résultats?

J’utilise .NET 4.5 et Visual Studio 2012.

Messagerie Internet uniquement, le moyen le plus simple de créer un serveur COM hors processus en C # consiste à utiliser un processus de substitution DLL .

Vous êtes toujours limité à deux interfaces ( ComInterfaceType.InterfaceIsDual ) et vous devez enregistrer la bibliothèque de types générée (et le faire également dans le cadre du déploiement):

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

Cela vous permettra d’utiliser le marshaller de bibliothèque de type COM, car vous ne disposez pas d’une DLL proxy / stuf COM dédiée pour vos objects COM C #.

Veillez à utiliser le binary correct RegAsm.exe , selon le bit cible de votre assembly ManagedServer.dll . Ce qui précède suppose du code x86.

Voici un exemple de modèle de travail complet. Il prend en charge l’enregistrement de la mère porteuse:

 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 { ssortingng ComMethod(ssortingng 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 ssortingng ComMethod(ssortingng data) { return data; } // registration [ComRegisterFunction()] public static void Register(Type type) { var guid = type.GUID.ToSsortingng("B"); using (var appIdKey = Registry.ClassesRoot.CreateSubKey(@"AppID\" + guid)) { appIdKey.SetValue("DllSurrogate", Ssortingng.Empty); } using (var appIdKey = Registry.ClassesRoot.CreateSubKey(@"CLSID\" + guid)) { appIdKey.SetValue("AppId", guid); } } [ComUnregisterFunction()] public static void Unregister(Type type) { var guid = type.GUID.ToSsortingng("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); } } } 

Par défaut, les objects sont créés dans un appartement MTA. Par conséquent, les méthodes d’interface peuvent éventuellement être appelées sur n’importe quel thread. Vous devez donc implémenter la sécurité du thread.

Si vous avez besoin d’un thread STA avec une pompe de message dans le processus de substitution pour vos objects, vous pouvez le faire explicitement en implémentant un singleton d’usine et en utilisant CoMarshalInterThreadInterfaceInStream / CoGetInterfaceAndReleaseStream pour exporter des objects en dehors du thread STA ( cela peut être lié).

Autre point, votre code client COM doit utiliser CLSCTX_LOCAL_SERVER lors de la création de cette instance de ManagedComObject . Ce n’est pas le cas avec Activator.CreateInstance(Type.GetTypeFromProgID("Noseratio.ManagedComObject")) , qui utilise apparemment CLSCTX_ALL . Cela peut être facilement pris en charge:

 using System; using System.Runtime.InteropServices; namespace Client { class Program { static void Main(ssortingng[] 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; } } }