[C#] Probleem bij aanroepen unmanaged C++ DLL

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • 0rbit
  • Registratie: Maart 2000
  • Laatst online: 18-03-2021
Mijn situatie is als volgt. Ik heb in C++ een simpele DLL geschreven en deze redelijk uitvoerig getest middels een testprogramma in C++. Verschillende inputs, verschillende gewenste outputs, etc. Alles lijkt te werken. "Mooi!", dacht ik.

Het uiteindelijke doel is echter deze DLL vanuit C# aan te roepen. Hiertoe heb ik analoog aan het testprogramma in C++ een programma geschreven waarin de DLL vanuit C# wordt aangeroepen. Dit werkt op het eerste gezicht; Het aanroepen van de functie werkt en geeft de juiste resultaten bij gegeven input terug.

Een probleem doet zich echter voor zodra ik de functie daarna nogmaals aanroep vanuit C#. Iedere keer dat ik daarna de betreffende functie aanroep komen er dezelfde verkeerde resultaten uit, terwijl ik alle waarden voor het aanroepen van de functie initialiseer. (Let wel; dit probleem bestaat niet in testprogramma wat ik in C++ schreef).

Ik vermoed dat er dus iets mis is met mijn C# code, maar ik heb geen idee meer wat. Mogelijk zit het hem in het

Mijn C# code ziet er zo uit (ik heb omwille van de duidelijkheid niet-relevante zaken weggelaten):

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
namespace DLL_CS
{

    class Program
    {

        [DllImport("MyDLL.dll")]
        private static extern void DLLfunction(ref double[] data, ref int param1, out double result1, out int result2);
        
        static void Main(string[] args)
        {
            
            int numRuns = 100;

            int param1 = 10;
            double result1;
            int result2;

            for (int k = 0; k < numRuns; k++)
            {
                DLLfunction(ref data, ref param1, out result1, out result2);
            }
        }
    }
}


De code voor de DLL is als volgt;

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
extern "C" __declspec(dllexport) void getMaxVal(double *& data, int & param1, double & result1, 
int & result2);

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    return TRUE;
}


void DLLfunction(double *& data, int & param1, double & result1, int & result2)
{

       // Doe iets met data aan de hand van param1 en lever result1 en result2 terug
    
}



Heeft iemand een idee waar het mis gaat?

Ik ben geheel voldaan, dank u wel!


Acties:
  • 0 Henk 'm!

  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Verschillende calling conventions? De default van DllImportAttribute is winapi/stdcall en ik vermoed dat jouw c++ functie een andere calling convention heeft.

We adore chaos because we like to restore order - M.C. Escher


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 17:15

Haan

dotnetter

Moet je met ref parameters werken? Zou het kunnen daar het probleem zit? * Haan gebruikt ze eigenlijk nooit

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • 0rbit
  • Registratie: Maart 2000
  • Laatst online: 18-03-2021
LordLarry schreef op maandag 28 juli 2008 @ 12:36:
Verschillende calling conventions? De default van DllImportAttribute is winapi/stdcall en ik vermoed dat jouw c++ functie een andere calling convention heeft.
Hmja. Daar ga ik eens naar kijken. Bedankt voor de tip!
Haan schreef op maandag 28 juli 2008 @ 13:10:
Moet je met ref parameters werken? Zou het kunnen daar het probleem zit? * Haan gebruikt ze eigenlijk nooit
Het moet niet in het geval van de eerste twee parameters. De twee outputs wil ik graag zonder veel marshalling poespas middels een reference teruggeven. Wat betreft de array met data is een reference niet noodzakelijk, maar zeker wenselijk, omdat de array soms enkele megabytes kan beslaan en een reference dan veel sneller is dan een value.

Ik ben geheel voldaan, dank u wel!


Acties:
  • 0 Henk 'm!

Verwijderd

Niet gehinderd door enige kennis van C++, C# of DLL's: in je DLL heet je functie DLLfunc en in je C# roep je DLLfunction aan?

Acties:
  • 0 Henk 'm!

  • 0rbit
  • Registratie: Maart 2000
  • Laatst online: 18-03-2021
Verwijderd schreef op maandag 28 juli 2008 @ 14:05:
Niet gehinderd door enige kennis van C++, C# of DLL's: in je DLL heet je functie DLLfunc en in je C# roep je DLLfunction aan?
Oh, dat is een foutje in het stippen van de code ;) Ik zal het even aanpassen, want daar ligt het niet aan.

Ik ben geheel voldaan, dank u wel!


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Mr_Atheist schreef op maandag 28 juli 2008 @ 13:23:
[...]


Hmja. Daar ga ik eens naar kijken. Bedankt voor de tip!
klopt, hij moet __stdcall zijn, of je moet de CallingConvention bij de DllImport manueel instellen (op CallingConvention.Cdecl)
Het moet niet in het geval van de eerste twee parameters. De twee outputs wil ik graag zonder veel marshalling poespas middels een reference teruggeven. Wat betreft de array met data is een reference niet noodzakelijk, maar zeker wenselijk, omdat de array soms enkele megabytes kan beslaan en een reference dan veel sneller is dan een value.
Onzin. Een array is al een reference. Wat je nu dmv 'ref' kunt doen is de variabele naar een andere array laten wijzen, maar aangezien dat niet is wat je wilt (en het kwa marshalling ook wat problematisch wordt) kun je het maar beter weglaten.
Wat je wel moet doen, is [In, Out] als attributen erbij zetten, zodat bij eventuele marshalling naar een kopie die resultaten ook weer terugkomen.

[ Voor 34% gewijzigd door .oisyn op 28-07-2008 14:52 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • jmzeeman
  • Registratie: April 2007
  • Laatst online: 12-09 16:17
Waar komt je data array in c# vandaan? Uit je DLL of uit C#. Door het verschil tussen de c++ heap(waar alles op z'n plek blijft) en de managed heap(waar elk object verplaatst mag worden) kunnen er wel is wat probleempjes ontstaan.
Als je er van uitgaat dat je c++ functie iets aan het array verandert kan het ook zo zijn dat C# bij het aanroepen van de c++ functie een kopie van je array maakt waardoor de veranderingen niet worden opgeslagen(de kopie wordt immers weg gegooid). Als dit het geval is biedt de Marshall class (System.runtime.interop uit m'n hoofd) uitkomst deze bevat mogelijkheden om managed geheugen tijdelijk 'vast' te zetten.

Acties:
  • 0 Henk 'm!

  • 0rbit
  • Registratie: Maart 2000
  • Laatst online: 18-03-2021
.oisyn schreef op maandag 28 juli 2008 @ 14:17:
[...]

klopt, hij moet __stdcall zijn, of je moet de CallingConvention bij de DllImport manueel instellen (op CallingConvention.Cdecl)
Ik heb nu mijn C# aangepast en bij de import staan;

code:
1
[DllImport("MyDLL.dll", CallingConvention=CallingConvention.Cdecl)]


Dat verhelpt het probleem nog niet.
Onzin. Een array is al een reference. [s]Wat je nu dmv 'ref' kunt doen is de variabele naar een andere array laten wijzen, maar aangezien dat niet is wat je wilt (en het kwa marshalling ook wat problematisch wordt) kun je het maar beter weglaten.
Je hebt gelijk. Ik was even in de war met het statische geval. In het dynamische geval is het natuurlijk (net als bij het passen van een class als parameter een pass-by-reference).
Nope, sorry, even gechecked. De 'ref' heb je idd niet nodig om de reden die je noemt, maar wel om ervoor te zorgen dat de array als in/out parameter wordt behandeld - de aangeroepen functie mag dus de contents van de array wijzigen, en dat zul je ook terugzien in de aanroepende functie.
ref en out doen beide hetzelfde, met het verschil dat out garandeert dat er een assignment plaatsvindt en zodoende kun je dus ongeinitialiseerde parameters meegeven zolang ze maar op de out manier worden doorgegeven.

Ik vermoed een veel fundamentelere fout van mijn kant; ref double[] is niet equivalent met double *&.
jmzeeman schreef op maandag 28 juli 2008 @ 14:23:
Waar komt je data array in c# vandaan? Uit je DLL of uit C#. Door het verschil tussen de c++ heap(waar alles op z'n plek blijft) en de managed heap(waar elk object verplaatst mag worden) kunnen er wel is wat probleempjes ontstaan.
Als je er van uitgaat dat je c++ functie iets aan het array verandert kan het ook zo zijn dat C# bij het aanroepen van de c++ functie een kopie van je array maakt waardoor de veranderingen niet worden opgeslagen(de kopie wordt immers weg gegooid). Als dit het geval is biedt de Marshall class (System.runtime.interop uit m'n hoofd) uitkomst deze bevat mogelijkheden om managed geheugen tijdelijk 'vast' te zetten.
De data komt in principe uit C# zelf. De data hoeft daarna binnen de DLL in dit geval niet aangepast te worden (en wordt dit ook niet, alhoewel ik me scenarios van soortgelijke functionaliteit kan voorstellen waarin dit wel het geval zal zijn; iets om alvast rekening mee te houden!).

[ Voor 25% gewijzigd door 0rbit op 28-07-2008 15:03 ]

Ik ben geheel voldaan, dank u wel!


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Mr_Atheist schreef op maandag 28 juli 2008 @ 15:00:
ref en out doen beide hetzelfde, met het verschil dat out garandeert dat er een assignment plaatsvindt en zodoende kun je dus ongeinitialiseerde parameters meegeven zolang ze maar op de out manier worden doorgegeven.
Ik had m'n post alweer geedit trouwens.

Ik heb het niet over het C# keyword 'out', maar over de semantiek van de variabelen :). Een in-variabele is alleen bestemd voor invoer van de functie, een in/out variabele is zowel invoer als uitvoer. Je moet de attributen [In, Out] dus ook specificeren voor je array:

C#:
1
2
[DllImport("MyDLL.dll", CallingConvention=CallingConvention.Cdecl)]
private static extern void DLLfunction([In, Out] double[] data, ref int param1, out double result1, out int result2);


Het zal in jouw geval niet heel veel uitmaken, de array zal sowieso wel als zodanig direct aan de unmanaged code worden doorgegeven. Maar als je een type hebt die ook weer gemarshalled moet worden, zoals een string, dan wordt er per definitie een kopie gemaakt van de array, en zonder [Out] zullen de aanpassingen dan ook niet terug worden gemarshalled naar je C# applicatie.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • 0rbit
  • Registratie: Maart 2000
  • Laatst online: 18-03-2021
.oisyn schreef op maandag 28 juli 2008 @ 15:12:
[...]

Ik had m'n post alweer geedit trouwens.

Ik heb het niet over het C# keyword 'out', maar over de semantiek van de variabelen :). Een in-variabele is alleen bestemd voor invoer van de functie, een in/out variabele is zowel invoer als uitvoer. Je moet de attributen [In, Out] dus ook specificeren voor je array:

C#:
1
2
[DllImport("MyDLL.dll", CallingConvention=CallingConvention.Cdecl)]
private static extern void DLLfunction([In, Out] double[] data, ref int param1, out double result1, out int result2);


Het zal in jouw geval niet heel veel uitmaken, de array zal sowieso wel als zodanig direct aan de unmanaged code worden doorgegeven. Maar als je een type hebt die ook weer gemarshalled moet worden, zoals een string, dan wordt er per definitie een kopie gemaakt van de array, en zonder [Out] zullen de aanpassingen dan ook niet terug worden gemarshalled naar je C# applicatie.
Dat hele marshalling daar moet ik me nog maar eens in verdiepen als ik dit alles zo bekijk, want ik begrijp nog een aantal dingen slecht.

Overigens was de oplossing simpel; (zoals altijd achteraf)

In de DLL:

code:
1
2
extern "C" __declspec(dllexport) void DLLfunction(double * data, int param1, double & result1, 
int & result2);


En dan in C#:

code:
1
2
[DllImport("MyDLL.dll", CallingConvention=CallingConvention.Cdecl)]
        private static extern void DLLfunction(double[] data, int param1, out double result1, out int result2 );


Werkt als een tierelier en bij noemenswaardige hoeveelheden data is de C++ implementatie toch een tikje sneller dan een pure C# variant.

Ik ben geheel voldaan, dank u wel!


Acties:
  • 0 Henk 'm!

Verwijderd

Als je die DLL toch puur hebt om in je .NET software te gebruiken, dan raad ik je aan om een C++/CLI assembly te maken. Voor je .NET applicatie die hem gebruikt gewoon managed zoals elke andere assembly, terwijl je unmanaged C++ code in die assembly kan gebruiken.

Acties:
  • 0 Henk 'm!

  • 0rbit
  • Registratie: Maart 2000
  • Laatst online: 18-03-2021
Verwijderd schreef op maandag 28 juli 2008 @ 15:49:
Als je die DLL toch puur hebt om in je .NET software te gebruiken, dan raad ik je aan om een C++/CLI assembly te maken. Voor je .NET applicatie die hem gebruikt gewoon managed zoals elke andere assembly, terwijl je unmanaged C++ code in die assembly kan gebruiken.
Dat is een beetje het probleem, de DLL moet eigenlijk vanuit zowel C++ als C# benaderbaar zijn, omdat het geheel een handige verzamelplaats moet zijn voor allerhande nuttige algoritmen. Daarbij komt het vaak voor dat een nieuwe toepassing ineens de optimalisatie van een ander stuk code vereist dan voor een andere toepassing. Bedankt voor de tip!

Ik moet me er echt eens meer in verdiepen. Het vervelende daarbij is dat het eigenlijk niet eens mijn werk is om dit soort dingen te implementeren en dat er bij ons gewoon te weinig mensen aan zulke problemen werken. We hebben niet eens een echte full-time programmeur en iedereen doet dit er een beetje bij. Optimalisatie van het gehele spectrum aan functionaliteit is dan ook nauwelijks een mogelijkheid, alhoewel het zeker de mooiste oplossing zou zijn.

Ik ben geheel voldaan, dank u wel!

Pagina: 1