Ik wil vanuit een C#-applicatie een methode aanspreken in een Win32/C DLL. De C-header definieert de methode als volgt:
De documentatie bevat C++ voorbeeldcode voor die method:
Dit kun je natuurlijk niet 1:1 overnemen in C# want die kent het char datatype niet (toch niet in dezelfde context). Nu heb ik na wat opzoekwerk drie werkende oplossingen gevonden, maar ik zou graag weten welke de beste oplossing is.
Methode 1:
char* bestaat niet in C#, maar voor zover mij bekend is sbyte[] het meest aanverwante type uit C#. Dus:
Het werkt trouwens ook met byte[] in plaats van sbyte[] en dat bespaart de omzetting van sbyte naar byte, maar ik weet niet of dat altijd goed zal gaan. Tenslotte is char (C) signed en byte (C#) unsigned. Als dit de beste oplossing is, weet iemand dan of het veilig is om byte[] te gebruiken in plaats van sbyte[]?
Methode 2:
In principe is de buffer parameter een string, maar hij moet een gedefinieerde capaciteit hebben en by reference gepasseerd worden. Met het string-type lukt dat niet, maar wel met StringBuilder:
Een pak korter en leesbaarder, maar het voelt enigszins raar aan om hiervoor een StringBuilder object te gebruiken. Ik heb geen deftige tegenargumentatie - enkel een onderbuikgevoel...
Methode 3:
Als laatste methode doen we de marshalling zelf. De code lijkt op het eerste zicht wat complexer, maar het aanspreken van de methode is in principe wel eenvoudiger:
Alle drie deze methodes werken perfect voor mij, maar wat is nu de beste methode? Welke oplossing zou een professioneel programmeur kiezen? Of is er misschien een andere, betere oplossing die ik (nog) niet ken? Alvast bedankt voor alle advies!
C:
1
| void FAR PASCAL getText(unsigned int handle, char far *buffer); |
De documentatie bevat C++ voorbeeldcode voor die method:
C++:
"getText" neemt de char "buffer" van 64 posities als parameter. Na het uitvoeren van "getText" bevat "buffer" een ASCII tekst.1
2
| static char buffer[64]; getText(handle, buffer); |
Dit kun je natuurlijk niet 1:1 overnemen in C# want die kent het char datatype niet (toch niet in dezelfde context). Nu heb ik na wat opzoekwerk drie werkende oplossingen gevonden, maar ik zou graag weten welke de beste oplossing is.
Methode 1:
char* bestaat niet in C#, maar voor zover mij bekend is sbyte[] het meest aanverwante type uit C#. Dus:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| [DllImport("api.dll", CharSet = CharSet.Ansi)] public static extern void getText(IntPtr handle, sbyte[] buffer); ... sbyte[] buffer = new sbyte[64]; getText(handle, buffer); // Omzetting sbyte[] array naar byte[] array byte[] bytes = new byte[buffer.Length]; Buffer.BlockCopy(buffer, 0, bytes, 0, buffer.Length); // Omzetting byte[] naar string System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); string text = encoding.GetString(bytes); |
Het werkt trouwens ook met byte[] in plaats van sbyte[] en dat bespaart de omzetting van sbyte naar byte, maar ik weet niet of dat altijd goed zal gaan. Tenslotte is char (C) signed en byte (C#) unsigned. Als dit de beste oplossing is, weet iemand dan of het veilig is om byte[] te gebruiken in plaats van sbyte[]?
Methode 2:
In principe is de buffer parameter een string, maar hij moet een gedefinieerde capaciteit hebben en by reference gepasseerd worden. Met het string-type lukt dat niet, maar wel met StringBuilder:
C#:
1
2
3
4
5
6
7
8
9
10
| [DllImport("api.dll", CharSet = CharSet.Ansi)] public static extern void getText(IntPtr handle, StringBuilder buffer); ... StringBuilder buffer = new StringBuilder(64); getText(handle, buffer); // Omzetting StringBuilder naar string string text = buffer.ToString(); |
Een pak korter en leesbaarder, maar het voelt enigszins raar aan om hiervoor een StringBuilder object te gebruiken. Ik heb geen deftige tegenargumentatie - enkel een onderbuikgevoel...
Methode 3:
Als laatste methode doen we de marshalling zelf. De code lijkt op het eerste zicht wat complexer, maar het aanspreken van de methode is in principe wel eenvoudiger:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| [DllImport("api.dll", CharSet = CharSet.Ansi)] public static extern void getText(IntPtr handle, IntPtr buffer); public static string getText(IntPtr handle) { var pointer = Marshal.AllocHGlobal(64); try { getText(handle, pointer); return Marshal.PtrToStringAnsi(pointer); } finally { Marshal.FreeHGlobal(pointer); } return null; } ... string text = getText(handle); |
Alle drie deze methodes werken perfect voor mij, maar wat is nu de beste methode? Welke oplossing zou een professioneel programmeur kiezen? Of is er misschien een andere, betere oplossing die ik (nog) niet ken? Alvast bedankt voor alle advies!
A bus station is where a bus stops. A train station is where a train stops... On my desk I have a workstation.