J’ai commencé avec un système très sophistiqué de clients et de serveurs avec des références COM et d’autres éléments, et j’ai réduit mes capacités jusqu’à ce que je réalise que je ne parviens même pas à obtenir un exemple de code Microsoft pour une activation COM gratuite de l’enregistrement d’un serveur COM géré. écrit en C #.
Code serveur:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices.ComTypes; using System.Runtime.InteropServices; using System.ComponentModel; namespace ClassLibrary1 { [Guid("A7AC6D8C-FF17-4D2C-A3B1-2C8690A8EA04") ,ComVisible(true)] public interface IClass1 { [DispId(1)] ssortingng DummyFunction(ssortingng inputValue); } [Guid("81723475-B5E3-4FA0-A3FE-6DE66CEE211C"), ClassInterface(ClassInterfaceType.None), ComDefaultInterface(typeof(IClass1)), ComVisible(true)] public class Class1 : IClass1 { public ssortingng DummyFunction(ssortingng inputValue) { return inputValue.Subssortingng(0, 1) + " Inserted " + inputValue.Subssortingng(1); } } }
Code client VB6:
Dim c As ClassLibrary1.Class1 Set c = New Class1 MsgBox c.DummyFunction("Ben")
Code client C ++:
#include "stdafx.h" #import raw_interfaces_only using namespace ClassLibrary1; int _tmain(int argc, _TCHAR* argv[]) { IClass1Ptr p; HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); hr = CoCreateInstance(__uuidof(Class1), NULL, CLSCTX_INPROC_SERVER, __uuidof(IClass1), (void **)&p); if (FAILED(hr)) { _tprintf_s(_T("Error %x\n"), hr); CoUninitialize(); return 1; } _bstr_t b = _T("Bobby"); BSTR b2; p->DummyFunction(b, &b2); wprintf_s(L"%s\n", b2); p->Release(); CoUninitialize(); return 0; }
Les deux clients fonctionnent correctement lorsque je supprime tout le code COM Reg-Free et enregistre la ClassLibrary1.dll avec regasm / codebase.
Ensuite, je désenregistre ClassLibrary1 et tente d’introduire Reg-Free COM pour le client VB6 avec le fichier Project1.exe.manifest:
Et ClassLibrary1.manifest:
Maintenant, je reçois parfois l’erreur 429 (le composant ActiveX ne peut pas créer d’object) et parfois (inexplicablement) une erreur d’automatisation:
Erreur d’exécution ‘-2146234304 (80131040)’: Erreur d’automatisation
J’essaie ensuite d’introduire l’isolation COM dans le client C ++:
Maintenant, quand je lance le client C ++, la sortie est simplement
Erreur 800401f9
Après de nombreux essais utilisant différents exemples avec le support technique de Microsoft, j’ai identifié de nombreux pièges lors de la tentative d’implémentation d’un serveur COM géré avec un client COM C ++ non géré. Voici les informations essentielles dont je me souviens, qui peuvent être appliquées à l’exemple de code de la question pour s’assurer que cela fonctionne.
–
#define RT_MANIFEST 24 #define MANIFEST_RESOURCE_ID 1 MANIFEST_RESOURCE_ID RT_MANIFEST ClassLibrary1.manifest
Ajoutez ensuite une étape de pré-construction comme ceci:
"C:\Program Files (x86)\Windows Kits\8.1\bin\x86\rc.exe" "$(ProjectDir)ClassLibrary1.rc"
Ensuite, dans l’onglet “Application” du projet, modifiez les “Ressources” pour utiliser ClassLibrary1.res au lieu de “Icône et manifeste”. Mais cela pose des problèmes: premièrement, le chemin d’access à RC.EXE n’est pas facile à définir sans le coder en dur; Deuxièmement, les informations de version de AssemblyInfoCommon seront ignorées car les ressources du fichier RC remplacent totalement toutes les ressources Win32 générées par le compilateur .NET.
Une autre possibilité consiste simplement à séparer le fichier manifeste de la DLL COM du serveur et à ne pas l’intégrer en tant que ressource. J’ai lu que cela peut ne pas être fiable, mais cela fonctionne sur Windows 7 Enterprise 64 bits SP1.
ConsoleApplication1.exe.config
) qui définit le mode de chargement du fichier .NET. Pour .NET 4.5, j’ai vu ce travail: –
Alors que pour .NET 3.5, il semble que useLegacyV2RuntimeActivationPolicy doive être commuté:
Il est important de vérifier que toutes les versions du framework et du CLR sont synchronisées. Et il est important de comprendre que les versions CLR ne sont pas les mêmes que celles du framework. Par exemple, si vous souhaitez créer un serveur COM géré sur .NET 3.5, la version CLR (runtimeVersion) doit être “2.0.50727”, mais la version .NET (supportedRuntime) doit être “v3.5”.
Assurez-vous que la version cible du .NET Framework du serveur COM correspond à la version de supportedRuntime du client. S’il est créé à partir de la ligne de commande, il se peut que la version du framework ne soit pas extraite du fichier de projet Visual Studio (par exemple, si vous exécutez directement le compilateur C # de VB.NET au lieu d’appeler MSBuild), assurez-vous que la construction cible la bonne version du framework.
Je n’ai pas encore validé tout ce qui précède, mais j’ai l’intention de passer rapidement en revue tout ce processus pour vérifier que j’ai tout attrapé. Voici ce que j’ai fini avec ce que je n’ai pas encore mentionné:
ConsoleApplication1.exe.manifest (dans le répertoire source, est copié ou incorporé dans le répertoire de sortie au moment de la construction)
ClassLibrary1.manifest
MODIFIER:
Maintenant, passez en revue et validez chaque détail avec les informations complètes du message d’erreur, etc.
Je commence par créer une solution unique contenant deux projets avec toutes les valeurs par défaut et le code indiqué dans la question. Je commence par aucun fichier de manifeste ni aucun des parameters de projet mentionnés dans la question, et j’appellerai explicitement lorsque je modifie le processus ci-dessous. Ce sont les étapes et les erreurs qui sont sur le chemin pour faire fonctionner ce projet.
tlbexp ClassLibrary1.dll
#import raw_interfaces_only
sur #import raw_interfaces_only
par des guillemets. Le texte est donc lu #import "ClassLibrary1.tlb" raw_interfaces_only
. Reconstruire: le succès. Error 80040154
(classe non enregistrée) car nous n’avons pas enregistré le composant ni configuré COM sans inscription. Error 800401f9
nous l’ Error 800401f9
et essayerons simplement de créer un manifeste client. Créez un nouveau fichier texte avec le contenu suivant et enregistrez-le sous le nom ConsoleApplication1.exe dans le répertoire du projet ConsoleApplication1: –
–
INFO: Parsing Manifest File C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ConsoleApplication1.exe. INFO: Manifest Definition Identity is ConsoleApplication1,type="win32",version="1.0.0.0". INFO: Reference: ClassLibrary1,version="1.0.0.0" INFO: Resolving reference ClassLibrary1,version="1.0.0.0". INFO: Resolving reference for ProcessorArchitecture ClassLibrary1,version="1.0.0.0". INFO: Resolving reference for culture Neutral. INFO: Applying Binding Policy. INFO: No binding policy redirect found. INFO: Begin assembly probing. INFO: Did not find the assembly in WinSxS. INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1.DLL. INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1.MANIFEST. INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1\ClassLibrary1.DLL. INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1\ClassLibrary1.MANIFEST. INFO: Did not find manifest for culture Neutral. INFO: End assembly probing. ERROR: Cannot resolve reference ClassLibrary1,version="1.0.0.0". ERROR: Activation Context generation failed. End Activation Context Generation.
–
La même erreur se produirait quand même si useLegacyV2RuntimeActivationPolicy était exclu dans ce cas. Malheureusement, je ne comprends pas tout à fait pourquoi, mais je suppose que cela a quelque chose à voir avec la nouvelle stratégie d’activation d’exécution v4.0 qui charge par défaut le chargement de CLR v2.0 si l’exécutable chargé ne fait pas explicitement référence à .NET 4.0 le code managé ne le fait pas car il ne fait pas explicitement référence à la période .NET).
sn -T ClassLibrary1.dll
partir d’une invite de commande du développeur. Après avoir mis à jour ClassLibrary1.manifest et ConsoleApplication1.exe.manifest, n’oubliez pas de reconstruire ConsoleApplication1.exe si le manifeste est incorporé et de copier ClassLibrary1.manifest dans le répertoire ConsoleApplication1.exe. Courir à nouveau et? publicKeyToken
et non un autre nom ridicule, comme privateKeyToken
; b) Assurez-vous que tous les atsortingbuts que vous avez spécifiés dans l’assemblyIdentity du manifeste côté serveur correspondent à ceux du manifeste côté client et que le type="win32"
n’est pas spécifié sur l’un mais pas sur l’autre. B Inserted obby
Je dois également noter que le client VB6 fonctionne également en utilisant les fichiers suivants avec le client VB6:
Project1.exe.config:
Project1.exe.manifest:
Cependant, il semble y avoir une tendance à signaler “Impossible pour le composant de créer un object ActiveX” (erreur d’exécution 429) dans de rares cas lorsque l’exécutable est créé et exécuté avant la création des fichiers de configuration et des fichiers manifestes. La reconstruction du fichier EXE semble résoudre le problème, mais je ne parviens pas à résoudre le problème. Il est donc difficile d’identifier une cause spécifique.
Je pensais résoudre assez bien les problèmes, mais quelque chose au sujet des nombreuses pièces mobiles et des nombreux codes d’erreur et messages d’erreur inefficaces signalés dans les problèmes de configuration de com-reg-free rend cette tâche presque impossible à comprendre sans une expérience solide, des connaissances internes ou une source Microsoft. code. Espérons que cette réponse aidera les autres à acquérir une expérience similaire. Veuillez prolonger cette réponse si vous en apprenez plus!
Le manifeste du serveur COM géré peut être correctement et facilement intégré si vous utilisez la commande “Ajouter” -> “Nouvel élément …” du projet pour append un “fichier de manifeste d’application”. Cela ajoute un fichier appelé app.manifest au projet. Mais ce qui est vraiment difficile, c’est qu’il ne peut pas être reproduit autrement via l’interface utilisateur de Visual Studio, si ce n’est par le biais d’une solution de contournement. Etant donné que le champ “Manifest” de l’onglet “Application” de la fenêtre de configuration du projet est désactivé pour les projets de type “Bibliothèque de classes”, le manifeste, qui serait normalement défini ici, ne peut pas être défini pour une bibliothèque de classes. Mais vous pouvez modifier temporairement le projet en une application Windows, modifier la sélection Manifest ici, puis le restaurer dans une bibliothèque de classes. Le paramètre va restr pour que le manifeste sélectionné soit correctement incorporé. Vous pouvez vérifier le réglage dans un éditeur de texte en affichant le fichier de projet. Chercher:
app.manifest
Erreur 0x80131040 peut toujours se produire avec toutes les précautions sockets ci-dessus. Pour vous aider à en déterminer l’origine, il est utile d’utiliser le visualiseur de journal de fusion pour obtenir plus d’informations sur ce qui se passe lorsque les assemblys sont chargés et résolus. Google “Fuslogvw” pour plus d’informations sur la façon d’afficher ce journal (fuslogvw.exe est un utilitaire fourni lorsque Visual Studio est installé). Il est également important de réaliser que cette application, par défaut, ne montre apparemment aucune information jusqu’à ce que vous la configuriez pour enregistrer les informations dans des fichiers, reproduire le problème, puis redémarrer l’application pour lire les fichiers journaux une fois qu’ils ont été générés. Et, selon la documentation MSDN, il est également important de vous rappeler d’exécuter cet utilitaire en tant qu’administrateur.
Une fois que vous avez passé tous les obstacles à l’exécution de fuslogvw.exe, il est possible que quelque chose comme ce soit affiché dans le journal:
WRN: Comparing the assembly name resulted in the mismatch: Major Version ERR: The assembly reference did not match the assembly definition found. ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.
Bien que le fichier de manifeste du serveur COM ait répertorié la version 1.0.0.0, il ne s’agit pas de la version (seule) utilisée lors de la liaison d’une référence client COM au serveur. Mon fichier EXE client essayait de référencer 1.0.0.0, ce qui correspondait exactement à la version du fichier manifeste du serveur COM, mais ne correspondait pas à la version .NET de la DLL. Après avoir corrigé les fichiers manifestes du client et du serveur afin de refléter la version actuelle dans la DLL du serveur .NET, l’erreur 0x80131040 a disparu et fuslogvw.exe a été la clé pour l’identifier en tant que source du problème.
Si le manifeste du client est synchronisé avec la version réelle de la DLL .NET, mais que le fichier de manifeste de la DLL du serveur ne reflète pas cette version, une erreur différente se produira:
L’application n’a pas pu démarrer car sa configuration côte à côte est incorrecte. Consultez le journal des événements de l’application ou utilisez l’outil sxstrace.exe de la ligne de commande pour plus de détails.
Erreur 0xc0150002 ou le message suivant peut être signalé:
L’application n’a pas pu démarrer correctement (0xc0150002). Cliquez sur OK pour fermer l’application.
J’ai vu cela se produire dans un cas où le manifeste du client était incorporé dans une DLL non gérée plutôt que dans un fichier EXE non géré et où l’élément assemblyIdentity
du manifeste ne correspondait pas exactement à l’ assemblyIdentity
du serveur. Le client contenait un processorArchitecture="x86"
supplémentaire processorArchitecture="x86"
que le serveur n’a pas spécifié, ce qui a provoqué une non-concordance. Malheureusement, je ne sais pas comment apprendre cela sans penser heureusement à vérifier les fichiers manifestes pour voir s’ils correspondent (ou à lire cet article). Cette erreur n’indique pas clairement que le fichier manifeste est la source du problème. Vous devez donc être conscient qu’il existe une corrélation possible entre ce message d’erreur et cette cause.
J’ai vu des fichiers de manifestes externes complètement ignorés, ce qui donnait un journal sxstrace complètement vide, même lorsque les exécutables impliqués n’avaient aucun manifeste. Cela peut apparemment se produire du fait du cache de contexte d’activation (un problème décrit à l’ adresse http://csi-windows.com/blog/all/27-csi-news-general/245-find-out-why-your-external). -manifest-is-being-ignored ). Pour contourner ce problème, vous pouvez utiliser la commande suivante pour toucher l’horodatage du fichier dont le manifeste est ignoré:
copy /b myfile.exe+,,
J’ai constaté une autre erreur difficile à expliquer, classe non enregistrée ( 0x80040154
– REGDB_E_CLASSNOTREG
), lors de l’appel de CoCreateInstance dans les conditions suivantes:
DllMain
si le commutateur /clr
n’est pas appliqué au fichier CPP ou lors de .cctor
si le commutateur /clr
est appliqué à la fichier. regasm
. CoCreateInstance
le commutateur /clr
est appliqué aux parameters du compilateur C ++. Si l’une des 3 dernières conditions est modifiée, le problème disparaît. (En outre, si la dernière condition est modifiée, vous pouvez obtenir un locking du chargeur en raison de la position n ° 1 – pour en savoir plus sur le locking du chargeur et sa relation avec CLR lors de l’ initialisation d’assemblages mixtes ). Par conséquent, si vous rencontrez une erreur de classe non enregistrée dans des circonstances similaires, demandez-vous si vous pouvez modifier l’une ou l’autre de ces 3 conditions pour résoudre l’erreur. Note: J’ai du mal à comprendre le comportement de # 6. Il semble que l’effet de changer cela dépend aussi de l’état de # 1. Cela ressemble à l’appel du constructeur (y compris de son CoCreateInstance
) après le chargement complet de la DLL, mais non de la classe, mais l’appel du constructeur lors de l’initialisation de la DLL aboutira si le commutateur /clr
n’est pas spécifié. Ma solution pour le moment consiste à recoder le fichier CPP client en C ++ géré, car il s’agissait d’une classe d’interface relativement simple entre le composant COM et le rest du code non géré. Alors maintenant, il n’y a plus de COM dans ce client, juste une référence .NET.
À toutes mes frustrations, l’explication de BlueMonkMN était vraiment utile mais j’ai quand même rencontré le message Class not registered
.
J’ai un .NET 4.6 COM interop Dll et un client C ++ qui utilise Registration Free COM au moyen de la technologie Windows SxS. Le manifeste d’assembly a été intégré à la DLL COM et le manifeste d’application a été placé à l’extérieur, sous la forme d’un fichier .manifest distinct.
Tout fonctionnait comme annoncé, mais le message Class not registered
commencé à CreateInstance
lors de la création de CreateInstance
lorsque l’un des CreateInstance
suivants se produit.
Dans cette situation, le journal sxstrace
était vide, le journal des événements de l’application ( eventvwr
) ne signalait aucune erreur SideBySide et le visualiseur de journal de liaison d’assemblage ( FUSLOGVW.exe
) ne montrait rien à partir de l’application client C ++.
Permettez-moi d’append un autre addendum à la réponse de BlueMonkMN.
Ce que je comprends de mes expériences jusqu’à présent, c’est que, dans un tel cas, le contexte d’activation de l’application client C ++ n’est pas créé.
Dans une telle situation, il est nécessaire de créer le contexte d’activation par eux-mêmes.
Le code suivant est placé dans l’application cliente C ++, après un appel à CoInitialize(0);
et avant de faire un appel à CreateInstance
.
ACTCTX context; memset(&context, 0, sizeof(context)); context.cbSize = sizeof(context); context.lpSource = L"CPPClient.exe.manifest"; context.lpAssemblyDirectory = L"D:\\code\\vs2017\\CPPClient\\Debug"; context.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID; HANDLE hActCtx = CreateActCtx(&context); ULONG_PTR cookie = 0; BOOL result = ActivateActCtx(hActCtx, &cookie); //Rest of the code goes here.
En tant que mesure de nettoyage, le morceau de code suivant est placé juste avant un appel à CoUninitialize();
DeactivateActCtx(0, cookie); ReleaseActCtx(hActCtx);
Maintenant ça sonne bien. Côte à côte fonctionne très bien. En cas de problème, vous pouvez le voir dans sxstrace, dans le journal des événements de l’application ou dans la visionneuse de journal d’assemblage.