C++ Methode in C#



  • hi ich hab ein problem mit einer dll die in c++ geschrieben ist.
    in der beschreibung steht:

    void* _stdcall Ethernet_Connect(cahr *chIP, char *chPort, unsigned int uiTimeOut)

    OutPut:
    void*: scuccess: Scanner object
    nosuccess: NULL

    die c# deklaration sieht so aus

    [DllImport("EthernetScanner.dll", EntryPoint = "EthernetScanner_Connect")]
    public static extern object EthernetScanner_Connect(string ip, string port, int timeout);

    nur schmeißt er den hier:

    System.Runtime.InteropServices.MarshalDirectiveException wurde nicht behandelt.
    Message="PInvoke-Einschränkung: Es können keine Varianten zurückgegeben werden."


  • Administrator

    Du erhälst auch kein Objekt zurück sondern einen Zeiger. Dazu gibt es in C# IntPtr . Zudem ist dein Timeout-Parameter ebenfalls von einem falschen Typ. Du hast in C# uint für unsigned int .

    Dein EntryPoint in C# stimmt zudem mit dem von dir gezeigten Funktionsnamen nicht überein. Aber ich nehme mal an, dass das nur ein Schreibfehler von deiner Seite ist.

    Grüssli



  • danke hat funktioniert

    nun hab ich jedoch ein weiteres problem

    unsigned int _stdcall GetInfo(structESInfo *structinfo)
    
    structESInfo *structinfo:
    typedef struct _ESI
    {
    <<-- variablen -->>
    } structESInfo;
    

    in c# sieht das bei mir so aus

    //methode dekl.
    [DllImport("EthernetScanner.dll", EntryPoint = "GetInfo")]
    public static extern uint GetInfo(ethernetscannerinfo infostr);
    
    //struktur
    public struct ethernetscannerinfo
    {
    // public variablen
    }
    
    //aufruf
    ethernetscannerinfo infostr = new ethernetscannerinfo();
    GetInfo(scannerobject, infostr, size, timeout);
    

    wahrscheinlich ist hier wieder irgendwo ein kleiner fehler



  • Das
    GetInfo(scannerobject, infostr, size, timeout);
    passt doch kein bisschen zur Signatur
    public static extern uint GetInfo(ethernetscannerinfo infostr);
    welche wiederum nicht zu
    *unsigned int _stdcall GetInfo(structESInfo structinfo)
    passt.

    Wenn Du die struct per Referenz übergeben willst und nicht per value, kannst du es so machen:
    GetInfo(structESInfo& structinfo) //C++

    und auf C# Seite
    GetInfo(ref ethernetscannerinfo infostr);

    Wenn Du auf C++ Seite bei Pointern bleiben willst, solltest Du Dir die bereits empfohlene IntPtr-struct anschauen.



  • dass auf c++ seite ist fest und kann icht nicht ändern

    c# seite sieht nun so aus:

    [DllImport("EthernetScanner.dll", EntryPoint = "EthernetScanner_GetInfo")]
    public static extern uint GetInfo(out IntPtr infostrptr);
    
    //infodaten lesen
    IntPtr strptr = new IntPtr(); ;
    EthernetScanner_GetInfo(out strptr);
    ethernetscannerinfo infostr = new ethernetscannerinfo();
    infostr = (ethernetscannerinfo)Marshal.PtrToStructure(strptr, strptr.GetType());
    

    jetzt wahrscheinlich komplett falsch



  • Auf die schnelle Zusammengeschrieben. Habe keine Lust das auf Deine Typen zu übertragen, aber ich denke Du kannst damit arbeiten.

    extern "C" __declspec( dllexport ) void Func(SpotsParameter* p);
    void Func(SpotsParameter* p)
    {
    	p->minArea = 23;
    }
    
    struct SpotsParameter
    {
     int minArea;
    };
    
    public struct SpotsParameter
    {
    	public int minArea;
    }
    
    public static class OpenCvUtil
    {
    	[DllImport("OpenCVImaging.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void Func(IntPtr p);
    }
    
    SpotsParameter p = new SpotsParameter();
    IntPtr ip = Marshal.AllocHGlobal(Marshal.SizeOf(p));
    Marshal.StructureToPtr(p, ip, false);
    OpenCvUtil.Func(ip);
    SpotsParameter p2 = (SpotsParameter)Marshal.PtrToStructure(ip, typeof(SpotsParameter));
    Marshal.FreeHGlobal(ip);
    
    MessageBox.Show(p2.minArea.ToString());
    


  • habs jetzt umgebaut - funktioniert trotzdem nicht variablen in struktur sind leer

    jetzt mal komplett.

    [DllImport("EthernetScanner.dll", EntryPoint = "EthernetScanner_GetInfo")]
    public static extern uint EthernetScanner_GetInfo(IntPtr scannerobject, IntPtr infostrptr, uint infostrgröße, uint timeout);
    
    //infodaten lesen
    uint size = Convert.ToUInt32(Marshal.SizeOf(typeof(ethernetscannerinfo)));
    
    ethernetscannerinfo es1 = new ethernetscannerinfo();
    IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(es1));
    Marshal.StructureToPtr(es1, ptr, false);
    
    EthernetScanner_GetInfo(scannerobject, ptr, size, timeout);
    ethernetscannerinfo es2 = (ethernetscannerinfo)Marshal.PtrToStructure(ptr, typeof(ethernetscannerinfo));
    Marshal.FreeHGlobal(ptr);
    

    der vollständigkeit halber noch die zeile aus der .h

    extern unsigned int _stdcall EthernetScanner_GetInfo(void *pEthernetScanner, structEthernetScannerInfo *structInfo, unsigned int uiInfoSize, unsigned int uiTimeOut);
    


  • 1. Ist die C#-Assembly für die gleiche Plattform kompiliert wie die DLL? (x86,amd64)
    2. Ist Dir der genaue Aufbau der ethernetscannerinfo-struct bekannst und hast Du das so in C# nachgebaut?
    3. Evtl musst Du Padding-Bytes und so ein Gedöns beachten, siehe: http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.structlayoutattribute.aspx



  • die struktur sieht in c++ so aus:

    structEthernetScannerInfo *structInfo:
    typedef struct _EtherNetScannerInfo
    {
       ....
       char chFirmwareName[...];
       usigned int ......
    } structEthernetInfo;
    

    in c# so

    // die struktur
    public struct ethernetscannerinfo
    {
        public string[] chSerienNummer; //= new string[/*serielnumbersize*/9];
        public string[] chFirmwareName; //= new string[/*fimwarenamesize*/6];
        public uint uiMBAnfang, uiMB, uiSBAnfang, uiSBEnde, uiMBLinMax, uiSBLinMax, uiMBNotLinMax, uiSBNotLinMax;
        public uint uiMBPixelMax, uiSBPixelMax, uiTemperatur, uiOperatinSeconsCounter, uiPowerCycleCounter, uiFiFo;
        public uint uiPositionEncoder, uiPositionEncoderDirection, uiProtokollVersion, uiLiniarisation, uiCameraMode;
        public uint uiProfileMode, uiScannerMode, uiShutterTimerForManuellMode, uiShutterTimeMaxForAutoMode;
        public uint uiPixelReadOutStart, uiPixelReadOutEnd, uiVideoGain, uiLaserIntensityThreshold, uiLaserTargetValue;
        public uint uiPeakWidthLimit, uiPeakThreshold, uiSynchronisation, uiProtocolVersion, uiShutterControl;
        public uint uiLiniarisation2, uiSpeed, uiFPGAVersion, uiDigitalInput, uiLaserValueOfProfile;
    }
    //ende-struktur
    

    sollte das gleiche sein oder?



  • Ev. hilft dir das Tool P/Invoke Interop Assistant weiter. Ein Artikel darüber gibt es hier bei MSDN.



  • matze94 schrieb:

    char chFirmwareName[...];

    public string[] chFirmwareName;

    sollte das gleiche sein oder?

    Nein, C++-char[] kannst Du nicht einfach in C#-strings[] mappen.
    Die MSDN hat viele Beispiele dazu.


Anmelden zum Antworten