Jag har ett dynamiskt länkbibliotek (I en DLL för Windows naturligtvis). Med en standard c-huvudfil till. Att skriva en simpel COM-wrapper i Visual Basic 6 var enkelt och ett effektivt sätt att låta VB- och VBScript-programmerare ta del av funktionaliteten. Struntsamma jag löste det med google, och dokumenationen för Mono (http://www.mono-project.com). Så det kompilerar like fint under Visual Studio på Windows som med Mono på så gott som vilken platform som hellst.ANSI/ISO C huvud till c#
Nu vill jag även låta .NET-utvecklare ha samma familjära enkelhet, men det verkar inte vara lika lätt. Jag vill skriva ett klassbibliotek i c#, och jag kan inte ändra länkbibliotekets funktion då det redan är i skarp drift.
Jag minimerar koden lite så låt oss säga att jag har detta c-huvud:
// Lite konstanter
#define NAME_LEN 16
// En välrigt enkel struktur
typedef struct {
int id;
char name[NAME_LEN + 1];
int stuff;
} session;
// Och en funktion som tar några grunargument
int login(session *sess, int orgnr, char *name);
Vad jag tänkt mig är att ha C API:t i en privat wrapper-klass i c# och anropen som statiska metoder på denna. Jag tror detta skulle dölja komplexiteten för slutanvändarna och förenkla bra för mig (Det har fungerat väl för Java, C++, Objective-C, Object Pascal, Perl och COM-wrappers som redan är skrivna iaf).
Problem #1:
#define NAME_LEN 16
Skulle kunna bli:
private const int NAME_LEN = 16;
Detta går inte att ha rakt i namnrymden. Så antingen som publik i wrapper-klassen eller som en privat enumeration i namnrymnden, vad är att föredra?
Problem #2:
Struktruren, något åt detta hållet kanske?:
[StructLayout(LayoutKind.Explicit)]
public struct session {
[FieldOffset(0)] public int id;
[FieldOffset(4)] public byte[] name = new byte[wrapperclass.NAME_LEN + 1];
[FieldOffset(24)] public int id;
}
Nu kör jag på byte då char är Unicode i c# och jag behöver LATIN1-teckenkodning. Men jag betvivlar att detta skall fungera iaf, jag antar att byte[] och new byte[...] gör en hel del automagi bakom kulisserna som är så långt ifrån ASNI/ISO C som man kan komma :). Så frågan lyder; Hur gör jag en struktur kompatibel med C i c#?
Problem #3:
Funktionsanropet, första rgumentet:
[DllImport("thelib.dll")]
public static extern int login(session* sess, int orgnr, byte *name);
Är syntaxkorrekt, men är det rätt väg? Jag har sett nyckelordet ref nämnas flera gånger, och det skulle kunna fungera lika bra här, är det en bättre metod? Jag kommer att skyffla runt denna struktur i programmet en hel del, det är ju som man kan gissa av namnet en sessions-struktur :). Så att skyffla runt en pekare verkar vettigare än att kopiera strukturen och skyffla runt på en hög data. Hur gör jag detta på ett c#-igt vis?
Problem #4:
Funtionsanropet sista argumentet:
[DllImport("thelib.dll")]
public static extern int login(session* sess, int orgnr, byte *name);
Jag gillar det inte alls! Klassen string har en metod ToCharArray, men det är Unicode och det var som jag skrev tidigare skrev inge att ha för min del. Så hur skickar jag strängar i vanlig LATIN1-teckenkodning till externa anrop i c#?
Många frågor så mpnga tack på förhand :).Sv: ANSI/ISO C huvud till c#
Om det är någon annan som kan tänkas vilja göra samma sak i framtiden så kommer min lösning här:
Problem #1:
För konstanter med #define i C fungerar en intern klass bra för att samla dem, och dölja för utomstående. För mitt exempel:
internal class Constants {
public const int NAME_LEN = 16;
}
problem #2:
C strukturer kompatibla med c#, för mitt exempel funger detta utmärkt (Strukturens medlemmar är fullt kompatible med vad GCC gör):
[StructLayout(LayoutKind.Sequential)]
internal struct session {
public int id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = Constants.NAME_LEN + 1)]
public byte[] name;
public int stuff;
}
Problem #3:
Inget problem egentligen ref skall uppenbarligen användas eftersom man tvingar användaren till att använda osäker kod (Hur nu detta definieras under Win32? :)) om man använder pekare.
Problem #4:
En vektor av byte kan användas att hjälpmetoder för textkodning från System.Text. Så funktionen deklareras som:
Problem #4:
Funtionsanropet sista argumentet:
[DllImport("thelib.dll")]
public static extern int login(ref session sess, int orgnr, byte[] name);
För att fixa en enkel omvandling från en vektor med byte med LATIN1 till string, och tillbaka kan dessa två hjälpmetoder användas:
public static byte[] StringToByteA(string str) {
if (str != null) {
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetBytes(str);
} else {
return null;
}
}
public static string ByteAToString(byte[] bytea) {
if (bytea != null) {
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
return enc.GetString(bytea);
} else {
return null;
}
}