none
Passare un puntatore Struct a una DLL RRS feed

  • Domanda

  • Premesso che tra codice gestito e quello non gestito mi stanno venendo i mal di testa.

    Ho la necessita da codice c# di richiamare una dll scritta in c++ (da scrivere), questo per avere una piu facile gestione del codice, considerando che i regolamenti e i punteggi cambiano molto facilmente di anno in anno.

    devo passare un puntatore ad una struttura e la funzione dovrebbe ritornare il contenuto della struttura stessa con i punteggi ed altro. Le ho provate di tutto e mi sono fermato, considerato che sembra impossibile riuscirci, almeno per quello che riguarda le mie misere conoscenze. ma l'ultimo errore che ricevo è il seguente

    Impossibile effettuare il marshalling di 'parameter #1': I puntatori non possono fare riferimento a strutture per le quali è stato eseguito il marshalling. Utilizzare ByRef.

    Premesso che ByRef viene utilizzato in visual basic ma proprio non riesco a comprendere l'errore. Questo il codice c#

    //[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    unsafe public struct Punti
    {
        fixed char points[4];
        fixed char mult[5];
        fixed char mult2[5];
    }
    
    unsafe internal static class NativeMethods
    {
        [DllImport("Arrl.dll")]
        internal static extern void CalcolaPunteggiArrlContest(Punti *pt);
    
        //Set diretory di ricerca della DLL in //MyApplication//System
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern bool SetDllDirectory(string lpPathName);
        
    }
    
    public class App
    {
        unsafe public static void CallDll(string Path)
        {
            Punti pt = new Punti();
            NativeMethods.SetDllDirectory(Path);
            NativeMethods.CalcolaPunteggiArrlContest(&pt);
        }
    }

    e questa è la dll vuota, considerando che non accetta niente è inutile perdere tempo con la scrittura del codice.

    // Arrl.cpp : definisce le routine di inizializzazione per la DLL.
    //
    
    #include "stdafx.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    typedef struct Punti
    {
        char points[4];
        char mult[5];
        char mult2[5];
    } *PUNTI;
    
    extern "C" void CalcolaPunteggiArrlContest(PUNTI *pt)
    {
    	
    }

    per il momento la funzione non ritorna niente. Considerato che non riesco ad andare avanti con il solo punttore.......

    Chi mi da una mano per capire dove sbaglio??  (tutto)

    venerdì 18 ottobre 2019 07:58

Risposte

  • Ciao,

    dal snippet iniziale a quello attuale hai cambiato un po di cose:

    -da esserre stringhe a lunghezza fissa sono diventate a lunghezza variabile

    - il tipo std::string non è compatibile con il mondo net devi utilizzare char* per le stringhe ANSI e wchar_t* per le UNICODE.

    le strutture corrette sono:

    typedef struct Punti
    {
    	char* points;
    	char* mult;
    	char* mult2;
    } *PUNTI;
    
    extern "C" __declspec (dllexport)  void CalcolaPunteggiArrlContest(PUNTI pt)
    {
    	pt->points="5";
    	pt->mult="abc";
    	pt->mult2="abc2";
    }
    
    
            public struct Punti
            {
                [MarshalAs(UnmanagedType.LPStr)]
                public string points;
                [MarshalAs(UnmanagedType.LPStr)]
                public string mult;
                [MarshalAs(UnmanagedType.LPStr)]
                public string mult2;
            }


    martedì 22 ottobre 2019 08:48

Tutte le risposte

  • Buongiorno,

    da quello che ho capito sembra la libreria c++ è in modalità standard c++.

    Se  lo  standard c++  non è un requisito del progetto, potresti valutare di creare una libreria ci C++/CLI che permette miscelare codice management e codice nativo, questo semplifica molto la scrittura.

    Io personalmente l'ho usato anni  fa con successo.

    Qui trovi i riferimenti al C++/CLI.

     

    venerdì 18 ottobre 2019 09:02
  • Gli do sicuramente un'occhiata, ma quando si tratta di iniziare a comprendere altre cose diventa tutto piu difficile.

    il problema grosso è quello di avere in seguito una facile manutenzione. Per intenderci le gare (contest) che dovrebbe contenere il programma sono circa 60 e le dll che ne dovrebbero uscire fuori almeno una decina racchiudendole per gruppi, ecco il motivo per cui la manutenzione e di conseguenza la distribuzione sarebbe molto piu semplificata senza la necessita di redistribuire l'intero progetto .exe

    venerdì 18 ottobre 2019 10:58
  • Il mio suggerimento era solo quello di realizzare le librerie in C++/CLI col vantaggio che sono più consumabili dal mondo .net.

    In ogni caso se non vuoi cambiare investire il tuo tempo nello studia C++/CLI per risolvere il tuo problema dovresti modificare cosi il tuo codice:

    // Arrl.cpp : definisce le routine di inizializzazione per la DLL.
    //
    
    #include "stdafx.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    __declspec(dllexport) typedef struct Punti
    {
    
    	char points[4];
        char mult[5];
        char mult2[5];
    } *PUNTI;
    
    extern "C" __declspec (dllexport)  void CalcolaPunteggiArrlContest(PUNTI pt)
    {
    
    	pt->points[0] = 'a';
    	pt->points[1] = 'b';
    	pt->points[2] = 'c';
    	pt->points[3] = 'd';
    
    }

        unsafe public struct Punti
            {
                public fixed char points[5];
                public fixed char mult[5];
                public fixed char mult2[5];
            }
    
            unsafe internal static class NativeMethods
            {
                [DllImport("Arrl.dll")]
                internal static extern void CalcolaPunteggiArrlContest(ref Punti pt);
    
                //Set diretory di ricerca della DLL in //MyApplication//System
                [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
                internal static extern bool SetDllDirectory(string lpPathName);
    
            }
    
            unsafe public static void CallDll(string Path)
            {
                NativeMethods.SetDllDirectory(Path);
    
                Punti punti = new Punti();
    
    
                NativeMethods.CalcolaPunteggiArrlContest(ref punti);
    
                Console.WriteLine(punti.points[0]);
            }


    venerdì 18 ottobre 2019 12:56
  • ti ringrazio per aver visto il codice. Vedo una dichiarazione differente nella dichiarazione della struttura nella parte dll. Domani provo ma ho voluto ringraziarti questa sera.

    Per quanto riguarda spendere tempo, non sono piu un giovincello, ti dico solo che ho iniziato con la programmazione in linguaggio macchina con il 6502 processore del commodore 64. Puoi fare facilmente i conti. Molti concetti saranno sicuramente comuni, ma la programmazione si è talmente evoluta che oramai correre dietro lo lascio alle nuove leve, con mente lucida e vulcanica. Oggi lo faccio per divertimento e per tenere la mente occupata.

    Comunque grazie di tutto

    venerdì 18 ottobre 2019 17:29
  • purtroppo niente da fare questo il codice di errore che mi arriva quando chiamo la funzione nella dll

    Una chiamata alla funzione PInvoke 'WincontestPro!WincontestPro.Strutture+NativeMethods::CalcolaPunteggiArrlContest' ha sbilanciato lo stack. Questo problema può verificarsi quando la firma PInvoke gestita non corrisponde alla firma di destinazione non gestita. Verificare che la convenzione di chiamata e i parametri della firma PInvoke corrispondano alla firma di destinazione non gestita.

    da quello che capisco sembra che il parametro passato non abbia lo stesso tipo


    • Modificato Birillo150 sabato 19 ottobre 2019 07:18
    sabato 19 ottobre 2019 07:17
  • purtroppo niente da fare questo il codice di errore che mi arriva quando chiamo la funzione nella dll

    Una chiamata alla funzione PInvoke 'WincontestPro!WincontestPro.Strutture+NativeMethods::CalcolaPunteggiArrlContest' ha sbilanciato lo stack. Questo problema può verificarsi quando la firma PInvoke gestita non corrisponde alla firma di destinazione non gestita. Verificare che la convenzione di chiamata e i parametri della firma PInvoke corrispondano alla firma di destinazione non gestita.

    da quello che capisco sembra che il parametro passato non abbia lo stesso tipo


    Quale versione dei compilatori utilizzi? 
    sabato 19 ottobre 2019 07:56
  • Quella di visual studio 2012

    ma posso provare anche con visual studio 2017

    • Modificato Birillo150 sabato 19 ottobre 2019 09:24
    sabato 19 ottobre 2019 09:22
  • Ho provato con visual studio 2017

    e da lo stesso errore

    sabato 19 ottobre 2019 09:57
  • Ciao,

    scusa il ritardo nella risposta. Ho fatto delle prove, l'esempio che ti avevo postato è per il <= .NET 3.5, con versioni superiori fallisce, il motivo che hanno cambiato le calling convention di default.

    Per risolvere il problema e sufficiente dichiara come  l'importazione della funzione:

                [DllImport("Arrl.dll", CallingConvention = CallingConvention.Cdecl)]
                internal static extern void CalcolaPunteggiArrlContest(ref Punti pt);

    lunedì 21 ottobre 2019 13:32
  • non ti scusare per il ritardo, ci sono cose piu importanti nella vita che queste frivolezze.

    Ho provato anche questo ma niente da fare stavo lavorando con la versione 4.5 net e sono passato alla versione 4.0, ma non posso scendere oltre perch+ ho alcuni componenti come C1Ribbon e truedbgrid che utilizzando la versione 4.0

    Ho fatto varie prove eliminando varie voci ma niente da fare.

    Ho provato anche a creare un nuovo progetto completamente vuoto (1 solo form senza controlli) con visual studio 2012 utilizzando il net 4.0 ma nonostante tutto e provando varie soluzioni, non riesco a venirne a capo.

    Ho preparato un file .rar ma non so come metterlo qui. Sto aspettando un amico che mi dia le credenziali sul server che abbiamo (sembrano cambiate) ed appena lo scarico sul server ti accludo l'indirizzo con un messaggio qui. Purtroppo non so piu cosa fare eppure ho una dll che funziona benissimo con un'altro programma.

    lunedì 21 ottobre 2019 17:22
  • eccoti il link dove puoi scaricare il programmino

    http://caravan-forum.it/portolano/provaDLL.rar

    lunedì 21 ottobre 2019 20:33
  • Ciao,

    dal snippet iniziale a quello attuale hai cambiato un po di cose:

    -da esserre stringhe a lunghezza fissa sono diventate a lunghezza variabile

    - il tipo std::string non è compatibile con il mondo net devi utilizzare char* per le stringhe ANSI e wchar_t* per le UNICODE.

    le strutture corrette sono:

    typedef struct Punti
    {
    	char* points;
    	char* mult;
    	char* mult2;
    } *PUNTI;
    
    extern "C" __declspec (dllexport)  void CalcolaPunteggiArrlContest(PUNTI pt)
    {
    	pt->points="5";
    	pt->mult="abc";
    	pt->mult2="abc2";
    }
    
    
            public struct Punti
            {
                [MarshalAs(UnmanagedType.LPStr)]
                public string points;
                [MarshalAs(UnmanagedType.LPStr)]
                public string mult;
                [MarshalAs(UnmanagedType.LPStr)]
                public string mult2;
            }


    martedì 22 ottobre 2019 08:48
  • Evidentemente vi è qualche cosa settato male in visul studio anche se l'altra dll (quella del path) la chiama e ritorna regolarmente come true. Il problema e che adesso quando chiama la dll incriminata, non ritorna niente o meglio non leggo niente della struttura (tra l'altro non ne ho nemmeno il tempo) perchè l'applicativo esce senza nessun errore e si ferma.

    ti ringrazio per il tempo speso ma a questo punto ci rinuncio e creo una dll direttamente in net (cosa che tra l'altro gia sto facendo e che dialoga bene senza problemi.

    Se solo per passare un puntatore sto avendo questi problemi, immagino cosa succederà in seguito quando a funzione completa, devo passare altre 2 strutture ed una tabella di database. Secondo me mi si rompe il processore.

    Si è vero che ho cambiato delle cose dalla funzione iniziale ma in effetti ho voluto provare con quello che dovrei passare realmente (in parte) dopo aver tentato con char e via discorrendo.

    Ciao e grazie per il tuo tempo
    martedì 22 ottobre 2019 11:53