2009-08-19 3 views
1

Ich schreibe ein Visual Studio-Add-in und muss ein verwaltetes CodeElements-Objekt zu seinem nicht manipulierten Formular marshallen. Ich brauche nur den Zeiger im Speicher, da ich ihn umwandeln und wie ein CodeElement auf der nicht verwalteten Seite behandeln kann.Marshalling CodeElements zu einem IntPtr in C#

Ich weiß, wie mit dem XML umzugehen, und ich habe eine funktionierende Version von diesem in C#. Ich erstellte die nicht verwaltete DLL, weil das Aufrufen aller verschiedenen Elementvariablen auf der untersten Rekursionsebene die Geschwindigkeit des Programms zunichte machte. Ich muss einfach wissen, wie System.Runtime.Interop.Marshal verwendet wird, um das CodeElements-Objekt in einen Zeiger auf das COM-Objekt im Speicher zu konvertieren.

Danke.

+0

DTB scheint nahe der Antwort, aber es scheint nicht zu sein den Zeiger richtig ausrichten. Wenn Objektreferenz nicht auf Instanz eines Objekts oder Fehler im geschützten Speicher gesetzt ist, müssen Sie noch etwas mit dem Marshalling ändern. – user142350

Antwort

3

Sieht aus wie Jonathan nahe war. Dies ist, wie ich es tun würde:

[DllImport("CodeMethodsToString.dll")] 
[return: MarshalAs(UnmanagedType.BStr)] 
private static extern string CodeMethodsToString(IntPtr functionObject); 

public static void CodeMethodsToXML(XmlElement parent, CodeElements elements) 
{ 
    GCHandle pin; 
    try 
    { 
     pin = GcHandle.Alloc(elements, GCHandleType.Pinned); 
     string methods = CodeMethodsToString(pin.AddrOfPinnedObject()); 
    } 
    finally 
    { 
     pin.Free(); 
    } 
} 
+0

Das ist der Weg. Fixieren Sie das Objekt, um zu verhindern, dass GC den Zeiger bewegt, um AccessViolationException zu vermeiden. –

+0

Ich bin verwirrt - ich kann das nicht in meinem Test-Kabelbaum arbeiten. Ich bekomme eine ArgumentException auf GCHandle.Alloc für eine RCW, die nicht vom Typ CodeElements ist. Sind die CodeElements von Visual Studio keine TLBImped-COM-Schnittstelle? –

+0

@Kim testen Sie immer noch mit der DoStuff-Implementierung in Ihrer Antwort? – scottm

1

Ist CodeElements eine ComVisible Schnittstelle und hat eine GuidAttribute?

Dann C# wird die Rangierung von COM-Objekten für Sie tun, und Sie können einfach CodeElements als Argument-Typ:

[DllImport("example.dll")] 
private static extern void DoStuff(CodeElements codeElements); 
+1

Angst, es ist nicht so einfach: CodeElements ist abstrakt. Ich würde einen Zeiger auf das Objekt benötigen. Wie würde ich das bekommen? – user142350

+1

Um zu verdeutlichen: Wenn es dieses Standard-Marshalling tut, ist es nach Wert? Gibt es eine Möglichkeit, es zu tun, ist es Marshalling Magie, sondern nur einen Zeiger übergeben? – user142350

+0

Es ist als Referenz übergeben. Ich nehme an, dass "CodeElements" darauf verweist: http://msdn.microsoft.com/en-us/library/envdte.codeelements.aspx Also sollte mein Beispiel einfach funktionieren. Wenn nicht, bitte die Fehlerdetails bekannt geben. – dtb

1

Sobald Sie einen Stern oder kaufmännisches Und sehen Sie, indem man es anfangen sollte ref (sichere Version eines Zeigers). Ich habe auf magische Weise Bezug genommen Arten anfangen zu arbeiten, wenn ich das Schlüsselwort ref in der Vergangenheit verwendet (was sehr widersprüchlich ist - aber ich denke, es ist eine jener Interop Dinge):

[DllImport("example.dll")] 
private static extern void DoStuff(ref CodeElements codeElements); 

Sie könnten auch versuchen:

[DllImport("example.dll")] 
private static extern void DoStuff([In, Out] ref CodeElements codeElements); 

Oder eine der Permutationen dieser Attribute.

Eine Sache, die Sie versuchen sollten, ist die Verwendung der MFC (ich denke, eine lange Zeit seit C++), um die COM-Bibliothek zu machen. Verwenden Sie keinen systemeigenen Aufruf export the thing as a type library, und fügen Sie es als eine Referenz in Visual Studio hinzu (ja, das ist so einfach). So landen Sie mit etwas nach oben wie:

myCoolClass.DoStuff(codeElements); 

Sie könnten auch sie anheften möchten (wenn Sie es an Pin benötigt, der Fehler intermittierend sein). Ich kann mich nicht daran erinnern, ob die RCW dass für Sie tun (ich bin fast sicher, es wird), so hier ist der Code, es zu tun:

GCHandle handle = new GCHandle(); 
try 
{ 
    handle = GCHandle.Alloc(fooz, GCHandleType.Pinned); 
    // Use fooz. 
} 
finally 
{ 
    if (handle.IsAllocated) 
    handle.Free(); 
} 
+2

'ref' wird verwendet, wenn Sie einen Werttyp (z. B. eine' Struktur') als Referenz und nicht als Wert übergeben möchten. "CodeElements" ist eine COM-Schnittstelle, d. H. Ein Referenztyp, und wird daher immer als Referenz übergeben. – dtb

+1

Ich habe das versucht und es hat nicht funktioniert, es könnte in einer Kombination der anderen Marshalling ich arbeiten. Danke, dass Sie mich daran erinnert haben, dass eines dieser Keywords relevant sein könnte. – user142350

+1

@dtb - lesen! Es hat gesagt, dass es in der Vergangenheit einen Unterschied gemacht hat; auch wenn es so scheint, wie es nicht sollte - die erzeugte MSIL ist NICHT identisch und die Interop-Ebene könnte das Keyword als passend sehen. In reinen .Net zu .Net-Calls würde es, einverstanden, keine Wirkung haben. –

1

Optional können Sie das Beispiel am Ende des Artikels finden Sie unter: GCHandle MSDN