none
managed / unmanaged code und Verwendung von IUnknown RRS feed

  • Frage

  • Hallo liebe Gemeinde,

    Ich hoffe ich bin hier richtig, weil mein Problem ein bisschen vielschichtiger ist. Ich werde mal versuchen, es möglichst ausgiebig zur erklären:

    Folgende Ausgangssituation:

    Ich bin dabei eine Visualisierung für ein CoDeSys-Projekt (CoDeSys ist eine Entwicklungsumgebung für die Programierung von SPSen nach IEC61131, was hier nur der Vollständigkeit halber erwähnt sein soll) zu entwickeln. Diese ist in der Lage ActiveX Elemente darzustellen. Mein Ziel ist es  mit Hilfe des VisioViewers von Microsoft ein Visio-Drawing darzustellen. 

    Nachdem ich VisioViewer auf dem entsprechenden Rechner installiert habe, konnte ich das ActiveX-Control in meiner Visualisierung auswählen, und wenn ich das Projekt starte wird der Viewer auch dargestellt.

    Um mit dem ActiveX-Control Daten auszutauschen gibt es die Möglichkeit eine eigene dll anzugeben, über die die Funktionen des ActiveX-Controls aufgerufen werden können. Diese dll muss dafür die folgende Funktion exportieren:

    void ExecuteActiveXCall(IUnknown* pUnk, char* pszId, char* pszParam, char* 
    pszReturnBuffer, int nReturnBufferSize, DWORD* pdwReturnFlag); 

    Die Parameter haben die folgende Bedeutung(Zitat aus der Dokumentation von Wago/CoDeSys):

    "pszId  :  im Feld Methodenidentifikation eingetragener IEC-String bzw. Stringvariable 

    pszParam :  im Feld Parameter eingetragener Wert

    Der Parameter pUnk ermöglicht eine Abfrage weiterer  Com(ActiveX-)Interfaces. Über diese können Sie beliebige Methoden über das gewählte ActiveX-Control aufrufen, wobei beliebige Parameter in einem String übergeben werden!

    Die Parameter pszReturnBuffer, nReturnBufferSize und pdwReturnFlag werden derzeit nicht 
    verwendet."

    Dazu gibt es ein Beispiel:

    #include "stdafx.h" 
    #include <unknwn.h> 
    #include <exdisp.h> 
    
    BOOL APIENTRY DllMain( HANDLE hModule,  
                           DWORD  ul_reason_for_call,  
                           LPVOID lpReserved 
      ) 
    { 
        return TRUE; 
    } 
    extern "C" __declspec (dllexport) void 
    ExecuteActiveXCall(IUnknown* pUnk, char* pszId, char* pszParam,  
                       
          char* pszReturnBuffer, int 
    nReturnBufferSize, DWORD* pdwReturnFlag) 
    { 
     if (strcmp(pszId, "IWebBrowser|GoBack") == 0) 
     { 
      IUnknown* pNewUnk; 
      IWebBrowser* pwb; 
      pUnk->QueryInterface(IID_IWebBrowser, (void**) &pNewUnk); 
      pwb = (IWebBrowser*) pNewUnk; 
      if (pwb) 
      { 
       pwb->GoBack(); 
       pwb->Release(); 
      } 
     } 
     else if (strcmp(pszId, "IWebBrowser|GoForward") == 0) 
     { 
      IUnknown* pNewUnk; 
      IWebBrowser* pwb; 
      pUnk->QueryInterface(IID_IWebBrowser, (void**) &pNewUnk); 
      pwb = (IWebBrowser*) pNewUnk; 
      if (pwb) 
      { 
       pwb->GoForward(); 
       pwb->Release(); 
      } 
     } 
    } 

    Was mit der beiliegenden fertig kompilierten dll funktioniert. Ich bekomme den Code auch übersetzt, jedoch zeigt die dll keine Funktion, aber das ist ein anderes Problem.

    Mein eigentliches Problem besteht darin, dass ich statt der ActiveX Komponenten IWebBrowser eine andere Komponente ansprechen will nämlich IViewer aus VViewer.dll des VisioViewers. Wenn ich den VisioViewer installiere kann ich in den Projekteigenschaften einen Verweis hinzufügen auf die VViewer.dll. Anschließend taucht dieser als Interop.VisioViewer.1.4 in der Verweisliste auf.

    auf das wesentilche reduziert sieht mein Code jetzt so aus:

    // Dies ist die Haupt-DLL.
    
    #include "stdafx.h" 
    #include <unknwn.h> 
    #include <comdef.h>
    #include "VisioActiveXWrapperDllCpp.h"
    
    #pragma unmanaged
    BOOL APIENTRY DllMain( HANDLE hModule,  
                           DWORD  ul_reason_for_call,  
                           LPVOID lpReserved 
      ) 
    { 
        return TRUE;
    	
    } 
    
    extern "C" __declspec (dllexport) void 
    ExecuteActiveXCall(	IUnknown*	pUnk, 
    					char*		pszId, 
    					char*		pszParam,                     
    					char*		pszReturnBuffer, 
    					int			nReturnBufferSize, 
    					DWORD*		pdwReturnFlag) 
    { 
      IUnknown* pNewUnk; 
    
      IViewer* piv;
    
    } 
    

    Wenn ich diesen Code zu übersetzten versuche bekomme ich die folgende Fehlermeldung:

    VisioActiveXWrapperDllCpp.cpp(30): error C3821: "VisioViewer::IViewer *": Ein verwalteter Typ oder eine verwaltete Funktion kann nicht in einer nicht verwalteten Funktion verwendet werden.
    VisioActiveXWrapperDllCpp.cpp(30): error C3699: "*": Diese Referenzierung kann nicht für den Typ "VisioViewer::IViewer" verwendet werden.
              Der Compiler ersetzt "*" durch ^", um die Analyse fortzusetzen.
    VisioActiveXWrapperDllCpp.cpp(40): error C3821: "void ExecuteActiveXCall(IUnknown *,char *,char *,char *,int,DWORD *)": Ein verwalteter Typ oder eine verwaltete Funktion kann nicht in einer nicht verwalteten Funktion verwendet werden.

    Ich hab jetzt eine menge gelesen über unmanaged und managed code, und wie ich den kombinieren kann, aber irgendwie bekomme ich es einfach nicht hin.

    Des weiteren habe ich versucht durch  Entfernen der  pragma Direktive "unmanaged" den Code zu übersetzen. Das klappt auch, jedoch scheitere ich dann daran, das IUnknown Objekt in ein IViewer-Objekt zu überführen. Dabei habe ich mit den Schlüsselwörtern __gc und __pin experimentiert, allerdings ohne Erfolg. Ich bekomme immer die Meldung

    VisioActiveXWrapperDllCpp.cpp(35): error C2440: 'Typumwandlung': 'IUnknown *' kann nicht in 'VisioViewer::IViewer ^' konvertiert werden

    oder etwas vergleichbares.

    ...ich bin mit meinem Latein am Ende, allerdings bin ich auch kein eingefleischter C++ programmieren, sondern komme eher aus der Automatisierungswelt...

    Für Anregungen und Hinweise wäre ich sehr dankbar :)

    Viele Grüße,

    Robert


    Mittwoch, 14. November 2012 09:57

Antworten

  • Du hast zwei Probleme:
    1. Der Aufruf Deiner Methode klappt nicht.
    Muss dies wirlich eine DLL Methode sein? Dann verwende eine def-Datei um den Namen auch wirklich so zu exportieren! Aktuell wird ein anderer Name exportiert und somit nie gefunden (siehe www.dependencywalker.com und schau Dir mal dort die exportierten Namen an).

    2. Warum willst Du managed un unmanaged code mischen?
    Das macht für mich keinen Sinn. Du kannst den VisoViewer auch direkt in unmanaged Code ansprechen...
    Siehe auch hier:
    http://blog.kalmbachnet.de/?postid=63


    Jochen Kalmbach (MVP VC++)
    Mittwoch, 14. November 2012 10:20
  • Schon mal vorab, ich hab's hinbekommen :)

    Es ist aus meinem ersten post wahrscheinlich nicht ganz raus gekommen, dass ich in der exportierten Funktion

    extern "C" __declspec (dllexport) void 
    ExecuteActiveXCall(IUnknown* pUnk, char* pszId, char* pszParam, char* pszReturnBuffer, int 
    nReturnBufferSize, DWORD* pdwReturnFlag) 
    

    einen Pointer auf das IUnknown Interface meines VisioViewer Objektes bekomme. In dem oben geposteten Beispiel wurde über QueryInterface daraus ein Interface auf IWebBrowser geholt, und anschließend auf IWebBrowser gecastet, damit danach die Methoden von IWebBrowser direkt zur Verfügung stehen. Ich hatte versucht, analog auf VisioViewer::IViewer zu casten, aber das funktionierte nicht, da VisioViewer::IViewer ein verwaltetes Objekt ist.

    Meine Lösung sieht jetzt so aus, dass ich mir über QueryInterface ein Pointer auf das IDispatch Interface des VisioViewer hole, und dann mit deiner Implementierung von Invoke weiter arbeite :)
    Freitag, 16. November 2012 15:26

Alle Antworten

  • Du hast zwei Probleme:
    1. Der Aufruf Deiner Methode klappt nicht.
    Muss dies wirlich eine DLL Methode sein? Dann verwende eine def-Datei um den Namen auch wirklich so zu exportieren! Aktuell wird ein anderer Name exportiert und somit nie gefunden (siehe www.dependencywalker.com und schau Dir mal dort die exportierten Namen an).

    2. Warum willst Du managed un unmanaged code mischen?
    Das macht für mich keinen Sinn. Du kannst den VisoViewer auch direkt in unmanaged Code ansprechen...
    Siehe auch hier:
    http://blog.kalmbachnet.de/?postid=63


    Jochen Kalmbach (MVP VC++)
    Mittwoch, 14. November 2012 10:20
  • Hallo Jochen, 

    danke für die schnelle Antwort!

    Das mit dem dependencywalker habe ich mir angesehen. Die exportierte Funktion scheint soweit in Ordnung zu sein, zumindest was den Namen betrifft. Allerdings ist die entstandenen dll deutlich kleiner als das Original...


    Auf die Möglichkeit den VisioViewer direkt im unmanaged Code zu verwenden bin ich bisher nicht gestoßen. Das wäre natürlich eine schöne Alternative. Ich werde es mir gleich mal ansehen, und dann Rückmeldung geben. 

    Gruß Robert

    Mittwoch, 14. November 2012 10:56
  • Also, ich bin mittlerweile schon ein bisschen weiter gekommen, aber noch nicht ganz am Ziel.

    Ich bekomme, wie in deinem Beispiel beschrieben, über folgenden Aufruf:

    HRESULT hr = CreateObject(OLESTR("VisioViewer.Viewer"), &pdispWord);

    eine Instanz vom VisioViewer gestartet, aber ich schaffe es nicht über

    hr = GetCOMObject(OLESTR("VisioViewer.Viewer"), &pdispWord);
    

    auf eine vorhandene Instanz zuzugreifen.

    ich habe analog mit "Shell.Explorer" probiert eine Instanz von IWebBrowser zu erzeugen, allerdings mit dem gleich Effekt. Als Ergebnis bekomme ich immer: 0x800401e3 Vorgang nicht verfügbar.

    Auch wenn ich unmittelbar vorher eine Instanz mit CreateObject erzeugt habe.

    Dein Beispiel konnte ich leider nicht 100% nachvollziehen, weil ich auf meinem Rechner kein Word installiert habe. 

    Aber es ist ganz egal, mit welchen OLE/COM Objekten ich es versuche, das Ergebnis ist immer das gleiche.

    BTW: Es gibt ein sehr schönes Tool, OLEView.exe in den "Windows Resource Kits", mit dem man sich alle registrierten OLE/COM objekte anzeigen lassen kann. 

    Ich frage mich, ob es nicht eine andere Möglichkeit als GetCOMObject(..) gibt, eine Referenz auf die vorhandenen OLE Instanz zu bekommen, insbesondere, da ich ja schon den Zeiger auf IUnknown geliefert bekomme.

    Ich muss aber gestehen, dass ich die zugehörigen Mechnismen noch nicht wirklich durchdrungen habe...

    Donnerstag, 15. November 2012 15:11
  • WAS ist denn das "IUnknown" das Du da bekommst? Auf WAS zeuigt das?
    Das Interface kannst Du normal direkt verwenden und dann dort die passenden Methoden aufrufen....


    Jochen Kalmbach (MVP VC++)
    Donnerstag, 15. November 2012 19:36
  • Schon mal vorab, ich hab's hinbekommen :)

    Es ist aus meinem ersten post wahrscheinlich nicht ganz raus gekommen, dass ich in der exportierten Funktion

    extern "C" __declspec (dllexport) void 
    ExecuteActiveXCall(IUnknown* pUnk, char* pszId, char* pszParam, char* pszReturnBuffer, int 
    nReturnBufferSize, DWORD* pdwReturnFlag) 
    

    einen Pointer auf das IUnknown Interface meines VisioViewer Objektes bekomme. In dem oben geposteten Beispiel wurde über QueryInterface daraus ein Interface auf IWebBrowser geholt, und anschließend auf IWebBrowser gecastet, damit danach die Methoden von IWebBrowser direkt zur Verfügung stehen. Ich hatte versucht, analog auf VisioViewer::IViewer zu casten, aber das funktionierte nicht, da VisioViewer::IViewer ein verwaltetes Objekt ist.

    Meine Lösung sieht jetzt so aus, dass ich mir über QueryInterface ein Pointer auf das IDispatch Interface des VisioViewer hole, und dann mit deiner Implementierung von Invoke weiter arbeite :)
    Freitag, 16. November 2012 15:26
  • > Meine Lösung sieht jetzt so aus, dass ich mir über QueryInterface ein Pointer auf das IDispatch Interface des VisioViewer hole, und dann mit deiner Implementierung von Invoke weiter arbeite :)

    Das hört sich doch gut an...


    Jochen Kalmbach (MVP VC++)
    Freitag, 16. November 2012 16:24