2010-11-17 18 views
5

sto avendo qualche problema con interoperabilità COM, la situazione è la seguente:utilizzare 32bit COM Server da un programma a 64 bit di .NET

a 32 bit COM Exe Server (che è stato programmato in C++) offre una classe con alcune funzioni membro che si occupano di hardware di terze parti (questo hardware lega anche il server COM Exe a 32 bit, poiché il produttore non supporta 64 bit).

Desidero utilizzare il server COM Exe a 32 bit in un'applicazione .NET (C#) a 64 bit ... All'inizio ho provato ad aggiungere un riferimento al server Exe in Visual Studio 2010 e ha creato un'interoperabilità -DLL. Questo Interop-DLL mi ha fornito le funzioni necessarie, uno dei quali è dichiarato come:

int Initialize(ref string callingApplicationPath); 

La dichiarazione originale in C++ si presenta così:

LONG Class::Initialize(BSTR* callingApplicationPath) 

... e come questo in IDL:

[id(1)] LONG Initialize([in] BSTR* callingApplicationPath); 

Tuttavia, quando voglio chiamare questa funzione da C# tramite l'Interop-DLL, si getta un BadImageFormatException. Sembra che l'Interop-DLL sia una DLL a 32 bit (forse c'è la possibilità di generare una DLL a 64 bit?).

Il mio prossimo tentativo è stato quello di istanziare il server Exe con questo codice:

Type type = Type.GetTypeFromProgID("OurCompany.Class"); 
Object o = Activator.CreateInstance(type); 
Object[] args = { Marshal.StringToBSTR(str) }; 
Object result = type.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, o, args); 

Questo codice, d'altra parte, getta un TargetInvocationException (In particolare: 0x80020005 (DISP_E_TYPEMISMATCH)) alla mia testa. Sfortunatamente non sono riuscito a scoprire quale tipo devo passare alla funzione da C# ... Ho provato tutte le funzioni StringToXXX nella classe Marshal ma niente sembra funzionare:/Immagino che mi manchi qualcosa di semplice qui ma non vedo cosa.

Qualsiasi aiuto è molto apprezzato!

migliori saluti

Christian

+0

Hai provato a lanciare Process Monitor e guarda cosa succede quando viene eseguita l'istanziazione? Forse non trova alcune voci di registro o qualche processo ha diritti insufficienti? Process Monitor migt aiuta con quello. – sharptooth

+0

@sharptooth: l'istanziazione stessa funziona bene e posso chiamare con successo un metodo fittizio che non accetta argomenti e restituisce un int. Il problema è "solo" la conversione System.String -> BSTR * – Christian

+0

che vedo. Qual è il punto di passaggio di BSTR * come parametro "in"? Perché non solo BSTR? – sharptooth

risposta

1

La dichiarazione IDL

[id(1)] LONG Initialize([in] BSTR* str);  

non ha senso. Quando si passa un BSTR come in parametro appena passare "per valore":

[id(1)] LONG Initialize([in] BSTR str); 

allora non avrà bisogno di fare nulla di speciale in codice C# - basta passare string lì e smistamento sarà fatto automaticamente.

Ovviamente dovrete cambiare anche la firma di implementazione del metodo.

1

Per impostazione predefinita, le stringhe .NET sono schierate da COM Interop a LPTSTR in C++. Pertanto è necessario effettuare il marshalling esplicito di un altro tipo di stringa non gestita (incluso BSTR) in e da una stringa .NET utilizzando l'attributo MarshalAs.
Prova

int Initialize([MarshalAs(UnmanagedType.BStr)] ref string callingApplicationPath); 
+0

Grazie per la tua risposta! Ma come incorporo questo codice nel mio programma C#? Nell'ultimo esempio di codice nella mia domanda non dichiaro alcuna firma di funzione, ma utilizzo InvokeMember. La prima riga di codice nella mia domanda è stata presa da Interop.DLL generato automaticamente. – Christian

+0

Vorrei modificare manualmente Interop.DLL generato automaticamente. – weismat

+0

Ho controllato la DLL Interop generata con .NET Reflector e ho scoperto che utilizza la firma che hai fornito. Tuttavia, chiamando il COM Exe Server tramite questa DLL, lo chiamo in-process e questo non funziona perché la mia app C# è a 64 bit e la DLL Interop + il server COM Exe sono entrambi a 32 bit. Ecco perché ho cercato di chiamare le funzioni out-of-process tramite InvokeMember, sfortunatamente senza successo:/ – Christian

0

A causa del common language runtime utilizzato da .net ci sono solo alcuni casi in cui è necessario distinguere tra 32 e 64 bit utilizzando il codice gestito. Tuttavia questo è vero solo per l'envoirement .net.Se si tenta di accedere a risorse non gestite, il formato bit è importante, poiché tutti gli indirizzi (interfaccia esportata) sono abbastanza statici e non compilati per 64 bit.

Ancora si potrebbe usare una costanza abbastanza semplice per raggiungere il tuo compito;
Creare un wrapper .net a 32 bit e collegarlo tramite wcf all'applicazione a 64 bit. Ti suggerisco di creare un wrapper C++ in modalità mista sul tuo server com/unmanaged e posizionare un layer basato su wcf scritto in clr "puro" (C#, vb.net, ecc.) Come punto di riferimento all'applicazione principale.

+0

Grazie anche per la risposta ... ma mi piacerebbe molto mantenere il numero di livelli il più basso possibile. E grazie all'aiuto di Sharptooth, sono riuscito a superare il mio problema iniziale :) – Christian

+0

non importa. non sapevo che avessi accesso anche alla parte del codice non gestito, dato che hai detto qualcosa sull'hardware di terze parti;) – Jaster

Problemi correlati