Maerl Brontius Posted March 14, 2014 Report Share Posted March 14, 2014 MC8.8 Ich bin dabei meine Algorithmen in eine C#-DLL auszulagern und habe mir entsprechend eine Interop-DLL in C++ geschrieben, die quasi als Wrapper für die C#-DLL agiert. In MC kann ich {self} als Referenz via IEasyLanguageObject übergeben und auch in C++ verwerten, nur scheitert es an der Weitergabe an C#. Die wichtigsten Punkte dazu folgend im Code. Lediglich die Übergabe von IEasyLanguageObject an C# ist so noch verkehrt. Möglicherweise ist die Lösung relativ einfach. Der MC-Support lieferte keine wirklich hilfreichen Informationen. Es wurde sogar die grundsätzliche Möglichkeit der objektorientierten Nutzung in den DLLs verneint. Ich habe dann auf das MC-Forum verwiesen, wo diesbezueglich die ersten Fragen bereits 2007 beantwortet und mit Support unterstüzt wurden. Wichtig ist die CLR-Unterstützung in der Interop-DLL. Das ist bei den Projekteigenschaften mit /clr konfiguriert. // ----- C++ Interop-DLL #include "stdafx.h" #include "TSLib_Interop.h" #include <comdef.h> #import "C:\Program Files\TS Support\MultiCharts64\PLKit.dll" no_namespace double __clrcall TestR(IEasyLanguageObject* pELObj, double _2) { return TS::Class1::TestR(pELObj, _2); } // ------ C# DLL using PLKit; public class Class1 { public static double TestR(ref IEasyLanguageObject elRef, double len) { return 2; } } Quote Link to comment Share on other sites More sharing options...
Maerl Brontius Posted March 16, 2014 Author Report Share Posted March 16, 2014 Mittlerweile hat sich einiges getan. Um in C# die Schnittstelle IEasyLanguageObject zu erreichen muss man diese via QueryInterface in Erfahrung bringen. Bis auf die auskommentierte C#-Methode Calc ist die Kommunikation so praktizierbar. Die restlichen Schritte in Calc konnte ich so nicht klären. Es liessen sich auch Mittel umsetzen in der C++-Schicht bestimmte Bar-Werte auslesen und diese dann auf einfache Weise zu übertragen. Das wäre aber keine elegante Art und Weise. Ein Zugriff auf den Indikator-Kontext auf C#-Ebene ist aber mehr als wichtig, da die C#-Lib umfangreicher wird und auch zur Weiterleitung bestimmter Informationen an eine C#-GUI genutzt werden soll, quasi eine bidirektionale Verbindung von EL C#-GUI mit einem Service als Dispatcher. Der offene Knackpunkt war noch das Weiterleiten der Referenz nach C#. Ein paar Gedanken weiter und ich habe mir MC.Net gezogen, ein bisschen eingelesen und meine Lib eingebunden, debugged, etc. Was mir neu war ist die Möglichkeit der License-Conversion für 199$, das Migrieren des EL-Codes ist einfacher als ich vermutet hatte und die ganze Infrastruktur, die ich mir aufbauen minimiert sich enorm. Der Vollständigkeit wegen habe ich den Code komplettiert, falls also jemand aus EL heraus auf C++ oder C# zugreifen möchte: // ----- Powerlanguage - Code ... EXTERNAL: "TSLib_Interop.dll", string, "StringExample", string ; EXTERNAL: "TSLib_Interop.dll", string, "Calc", IEasyLanguageObject, string, int ; ... resultCalc = Calc( self, "value9", BarNumber); Plot1(value9, "Calc", Blue); commentary("ResultCalc: " + resultCalc + newline + "Sponsored by: " + StringExample("-Next")); // ----- C++ TSLib_Interop.def // This file declares the functions to be exported from this C++-Interop-DLL // MC can call all functions exported here LIBRARY "TSLib_Interop" EXPORTS Calc StringExample // ----- C++ Interop-DLL #include "stdafx.h" #include "TSLib_Interop.h" #include <comdef.h> #import "C:\Program Files\TS Support\MultiCharts64\PLKit.dll" no_namespace // --- Two helper methods when dealing with <string> in C#-Method signature ----------- System::String^ bstr2string(const _bstr_t& _str){ if (!_str.length()) return System::String::Empty; return System::Runtime::InteropServices::Marshal::PtrToStringBSTR((System::IntPtr)(BSTR)_str); } const _bstr_t string2bstr(System::String^const _str){ if (System::String::IsNullOrEmpty(_str)) return ""; _bstr_t _result; _result.Attach( (BSTR)(void*)System::Runtime::InteropServices::Marshal::StringToBSTR(_str) ); return _result; } LPCSTR __stdcall Calc( IEasyLanguageObject* pEL, LPCSTR varName, int barnum ) { static _bstr_t s_result; // Example of accessing IEasyLanguageObject from within C++ IEasyLanguageVariable *pVar = pEL->Variables[ varName ] ; if ( pVar ) pVar->AsDouble[0] = (double) barnum; // Calling C#, the ref is handled by using IntPtr s_result = string2bstr(TS::Class1::Calc((System::IntPtr)pEL, barnum)); return s_result; } LPCSTR __stdcall StringExample(LPCSTR _input_str){ static _bstr_t s_result; s_result = string2bstr(TS::Class1::StringExample(bstr2string(_input_str))); return s_result; } // ------ C# DLL using PLKit; using System; using System.Runtime.InteropServices; namespace TS { public class Class1 { public static string StringExample(string _1){ return "Tom-" + _1; } public static string Calc(IntPtr elRef, double barnum) { // Bis hierher funktionieren die Aufrufe. Lediglich das Extrahieren der // Schnittstelle IEasyLanguageObject in C# fehlt. COM ist wirklich nicht meine Welt // aber so in etwa könnte es aussehen. Ich habe zwei Varianten aufgeführt, um an // die Schnittstelle zu gelangen. Welche davon die eigentliche ist // und wie das im Detail aussehen wuerde, die Frage bleibt im Raum, ist mir aber mittlerweile // unwichtig geworden ;) // IEasyLanguageObject mcObj; // Guid myGuid; // string infoTxt = ":"; // Variante 1 // IntPtr ptr = Marshal.GetIUnknownForObject(elRef); // result = Marshal.QueryInterface(ptr, ref iid, out finalIntPtr); // Variante 2 // ptr = Marshal.GetComInterfaceForObject(elRef, typeof(IEasyLanguageObject)); // ... Marshal.ReadIntPtr(ptr); // mcRef = (IEasyLanguageObject)Convert.ChangeType(ptr2, typeof(IEasyLanguageObject)); // Marshal.Release(ptr); // Marshal.Release(...); // return ...; } } } 2 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.