04. august 2007 - 00:18Der er
25 kommentarer og 1 løsning
VB datastruktur til C# .NET struct
Hej
Jeg er ved at lave en applikation som skal kommunikere med en anden applikation via et API.
Jeg har nogle eksempler i VB som jeg har oversat til C#. Men jeg får fejl ved nogle af kaldene vedr. memory.
Sådan ser VB definitionen ud:
Public Type Order lSize As Long ' Size of the entire structure ' Denne må settes til Len(Order) før Api-kallet sKey As Integer sOrdreType As Integer lKundeNr As Long lFiller1 As Long
achOrdreNr As String * 10 bNull_1 As String * 1 achOrdreDato As String * 6 bNull_2 As String * 1 achLevDato As String * 6 bNull_3 As String * 1 achKundeNavn As String * 30 bNull_4 As String * 1 achAdresseI As String * 30 bNull_5 As String * 1 achAdresseII As String * 30 bNull_6 As String * 1 achAdresseIII As String * 30 bNull_7 As String * 1 achPostNr As String * 6 bNull_8 As String * 1 achPostSted As String * 25 bNull_9 As String * 1 achDeresRef As String * 30 bNull_10 As String * 1 achVaarRef As String * 30 bNull_11 As String * 1 achLevBet As String * 30 bNull_12 As String * 1 achLevMate As String * 30 bNull_13 As String * 1 achOrganisasjonsNr As String * 15 bNull_14 As String * 1 achOrdreReferanse As String * 10 bNull_15 As String * 1 chFlag As String * 1 chRestJN As String * 1 chFiller1 As String * 1 sLagerSted As Integer sFiller1 As Integer sFiller2 As Integer sFiller3 As Integer lAvdeling As Long lProsjekt As Long lFiller2 As Long lFiller3 As Long dOrdreRabattPros As Double dKontantRabattPros As Double dOrdreSum As Double dValutaKurs As Double dSumValuta As Double usValutaKode As Integer sFiller4 As Integer lFiller4 As Long dEkspedisjonsGebyr As Double dFiller2 As Double achLevNavn As String * 30 bNull_16 As String * 1 achLevAdresseI As String * 30 bNull_17 As String * 1 achLevAdresseII As String * 30 bNull_18 As String * 1 achLevAdresseIII As String * 30 bNull_19 As String * 1 achLevPostNr As String * 6 bNull_20 As String * 1 achLevPostSted As String * 25 bNull_21 As String * 1 chFiller2 As String * 1 sLevAdrNr As Integer End Type
Her er min .NET udgave
[StructLayout( LayoutKind.Sequential, CharSet = CharSet.Ansi )] public struct Order { public int lSize; public short sKey; public short sOrdreType; public int lKundeNr; public int lFiller1; [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 10 )] public string achOrdreNr; // public string * 10 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_1; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 6 )] public string achOrdreDato; // public string * 6 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_2; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 6 )] public string achLevDato; // public string * 6 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_3; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 30 )] public string achKundeNavn; // public string * 30 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_4; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 30 )] public string achAdresseI; // public string * 30 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_5; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 30 )] public string achAdresseII; // public string * 30 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_6; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 30 )] public string achAdresseIII; // public string * 30 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_7; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 6 )] public string achPostNr; // public string * 6 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_8; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 25 )] public string achPostSted; // public string * 25 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_9; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 30 )] public string achDeresRef; // public string * 30 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_10; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 30 )] public string achVaarRef; // public string * 30 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_11; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 30 )] public string achLevBet; // public string * 30 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_12; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 30 )] public string achLevMate; // public string * 30 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_13; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 15 )] public string achOrganisasjonsNr; // public string * 15 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_14; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 10 )] public string achOrdreReferanse; // public string * 10 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string bNull_15; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string chFlag; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string chRestJN; // public string * 1 [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1 )] public string chFiller1; // public string * 1 public short sLagerSted; // Integer public short sFiller1; // Integer public short sFiller2; // Integer public short sFiller3; // Integer public int lAvdeling; // Long public int lProsjekt; // Long public int lFiller2; // Long public int lFiller3; // Long public double dOrdreRabattPros; // Double public double dKontantRabattPros; // Double public double dOrdreSum; // Double public double dValutaKurs; // Double public double dSumValuta; // Double public short usValutaKode; // Integer public short sFiller4; // Integer public int lFiller4; // Long public double dEkspedisjonsGebyr; // Double public double dFiller2; // Double ' NYTT 971
// Utvidelser til Build 920 // Alt over er som i ENORDREHODE
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct Nielle { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 3)] public string str1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 3)] public string str2; }
som køre igennem uden at fejle selv om strengene er længerer end 3.
Man kunne derfor forestille sig at værdierne, i din struct, ligger på nogle andre steder i hukommelsen end der hvor den modtagende metode forventer at de skal ligge. Dette er sikkert grunden til at du får memory-fejl.
Desværre kan jeg ikke selv give noget bud på kuren. :^|
Der skal sendes en ovenstående datastruktur til API'et som returnerer et handle (memory adresse antager jeg). Det handel bruges så når ordren skal gemmes. Det er når jeg prøver at lave "gemmekaldet" at API'et kommer med en dialog om at et memory område ikke kan findes.
arne_v> Men MarshalAs har vel kun effekt når man Marshal'er !?
Det har du jo nok ret i. :^)
Nu er det ikke meningen at jeg vil hijacke dette spørgsmål, men der er nu alligevel noget som under mig med dit eksempel:
1) Med denne kode:
private static byte[] ConvertStructToBytes(object o) { int siz = Marshal.SizeOf(0); ... byte[] res = new byte[siz]; ... return res; }
- vil ConvertStructToBytes() altid returnere et byte-array med 4 bytes (tallet 0 bliver tolket som en System.Int32). Hvordan kan den så håndtere structs som er væsentlig større end 4 bytes? For det kan den rent faktisk - har prøvet efter...
Det virker lidt mistænkeligt. Næsten som om at arrayet alligevel fylder mere end de 4 bytes (også selv om Length faktisk siger at længden er 4)?!
2) Hvordan kan det være at de 2 strenge - anden gang de bliver udskrevet via n2 - kun er 2 karakter lange i stedet for 3 (SizeConst=3)? Er det mon fordi at de et eller andet sted undervejs bliver nul-termineret, og at nul-byten "stjæler" det sidste tegn i strengen?
Mit gæt en af følgende: * AllocHGlobal allokerer memory på 8 byte boundaries og derfor vil alt 1-8 reelt give 8 byte reel plads * virtuel memory mappes per page og derfor kan man "bruge" pladsen op til 512 byte boundary (i dette tilfælde - i andre tilfælde vil man overskrive vigtige data)
Her er et eksempel på hvordan det kan gøres i et regneark.
Sub LagEnOrdre()
Dim lRetVal As Long Dim strONR As String Dim lKundeNr As Long Dim sTekstLinje As String * 60
With ActiveSheet Application.ScreenUpdating = False
If (OrdreHandle <> 0) Then ' Free ORDRE !!!!!!!! RubFreeEnOrdre (OrdreHandle) End If
OrdreHandle = 0
strONR = .Cells(6, 1) lKundeNr = .Cells(6, 2) If lKundeNr = 0 Then MsgBox (vbCr & "Kunde-nummer er IKKE fylt ut !!!" & vbCr) Else EOH.lSize = Len(EOH) 'Size of the entire structure, settes før Api-kalles
If (strONR = "") Then ' Opprett en ny ordre om den ikke finnes fra før ! EOH.achOrdreNr = "" EOH.lKundeNr = lKundeNr EOH.sOrdreType = 101
1) Har du prøvet at teste på res-værdierne undervejs? De indeholder formodentlig en kode om hvorvidt det gik godt ... returnere de alle sammen "success"?
2) Dit oprindelige spørgsmål gik på struct'en Order. Den kan jeg saelt ikke se blive brugt i dit kodeeksempel?
3) Din regneark-kode er lidt underlig: Du opretter aldrig noget OrdreLinje-objekt. I stedet begynder du blot at arbejde med EOL som om den allerede eksistere. Er dette en global defineret variabel, eller hvad?
Under alle omstændigheder er det en afvigelse mellem de to løsninger, hvis den ene hele tiden ændre sin adresse (i C# løsningen) og den anden ligger fast (i VBA-løsningen).
Det også lidt interessant at dit EnOrdreLinje sendes til RUP i linjen lige før at det går galt.
4) I VBA løsningen kalder du RubEnOrdreAddTekstLinje() imellem kaldene til RubEnOrdreAddLinje() og RubSkriveEnOrdre(). Det gør du ikke i C# løningen. Kunne det tænkes at give problemer?
5) Har du prøvet om det fungere bedre i VB.Net fremfor C#?
Arne v: Det med lave kald gennem en DLL er nyt for mig så jeg skal bruge et eksempel for at se hvad du har i tankerne ang. Marshal.AllocHGlobal og IntPtr.
Nielle: 1) Returværdierne er ok bortset fra RubOpprettEnOrdre som skal returnerer memory området for ordren som ser ud til at være forkert.
2) Jeg gav den bare et mere sigende navn. Det er det samme som EnOrdreHode2
3) Jeg har ikke prøvet at bruge eksemplet, det er blot et der er vist i dokumentationen.
Adressen der returneres i C# er den samme for RubOpprettEnOrdre
4) Det er ikke et nødvendigt kald
5) Jeg har overvejet at lave det i VB.NET da jeg så kan lave en del copy/paste fra eksemplerne jeg har. men jeg er mest til C#. Jeg kan vel lave typerne og kaldene til API'et i VB.NET dll og resten i C#
VB er endnu ringere en C# (hvilket jeg nu altid har ment...)
Virker: [DllImport("RubApi.dll",CharSet=CharSet.Ansi)] private static extern int RubRpcLogon(int client, string dataArea);
Virker ikke: Public Declare Function RubRpcLogon Lib "RubApi" (ByRef clientId As Integer, ByRef dataPath As String) As Integer
Virker ikke: <DllImport("RubApi.dll", CharSet:=CharSet.Ansi)> _ Public Shared Function RubRpcLogon(ByRef clientId As Integer, ByRef dataPath As String) As Integer End Function
Er det forkert at gemme returværdien fra API'et i en int, hvilket måske giver mig en forkert adresse? Andre forslag???
int handle = RubOpprettEnOrdre(ref ordreHode2)
[DllImport( "RubApi.dll", CharSet = CharSet.Ansi )] public static extern int RubOpprettEnOrdre(ref EnOrdreHode2 order);
Jeg kunne ikke gennemskue dit eksempel, det var adressen som API'et returnerede der var forkert hvis den belv lagret i en int. Det var det jeg havde mistanke om da jeg skrev 06/08-2007 21:30:34
Jeg lavede min int handle; om til int* handle; og nu virker det. Troede egenligt at det var det samme som ref, men så lærte jeg også noget i dag.
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.