Hallo!
Hab ein Problem und schon überall nach Lösungen geschaut, doch nichts gefunden, was mir weiterhelfen konnte.
Bevor ihr jetzt weiter lesen wollt:
Es handelt sich hier meiner Ansicht nach um ein sehr komplexes Problem!
Also bin gerade dabei eine Klasse zu schreiben, die Funktionen einer C-DLL aufruft, um mit einer SPS S5 zu kommunizieren.
Die C-Funktion in der DLL lautet:
C-Code
// <param name="rb">Zeiger auf einen anwenderdefinierten Requestblock.</param>
// <param name="hSRAccessPoint">Handle auf den übergebenen "Zugangspunkt der Applikation"</param>
int SRMD_Request(int hSRAccessPoint, RB *rb)
Eigentlich ein ganz normaler Aufruf, bis auf die Spizialität, dass ein Zeiger auf eine benutzerdefinierte Struktur als Übergabe Parameter gefordert wird.
Und von Zeigern hab ich relativ wenig Ahnung... ich hab zwar das Prinzip verstanden, doch da ich noch kein C/C++ vorher gemacht habe keine Erfahrung damit.
Ich habe den Aufruf in meinem Code folgendermaßen realisiert:
[DllImport("s7_sr.dll", SetLastError=true, CharSet = CharSet.Auto)]
private static extern int SRMD_Request(int hSRAccessPoint, ref RB rb);
Jetzt ist halt das Problem, dass die benutzerdefinierte Struktur passen muss, damit der Aufruf erfolgreich ist.
In einem C-Beispiel konnte ich sehen, wie das gemacht gehört:
C- CODE
/*****************************************************************************************
\*** Requestblock structures
\*****************************************************************************************/
#pragma pack(push, _SR_RB_alignment,1)
typedef struct /* RB-Header (length=16) */
{ DWORD rb_res;
BYTE rb_length;
WORD rb_user;
BYTE rb_resp_port;
DWORD rb_callback;
BYTE rb_susys;
BYTE rb_opcode;
WORD rb_response;
} SR_RBH;
#define SR_RB SR_RBH
typedef struct /* (length=16+2=18) */
{ SR_RBH rbh; /* Header */
WORD open_reference; /* Identifikation */
} SR_OPEN_REQ;
typedef struct /* (length=31) */
{ BYTE loc_nsap_id_len;
BYTE loc_tsap_id_len;
BYTE loc_tsap_id[8];
BYTE rem_net_addr_len;
BYTE afi;
WORD subnet;
BYTE host_id[6];
BYTE lsap_id;
BYTE rem_net_nsap;
BYTE rem_tsap_id_len;
BYTE rem_tsap_id[8];
} SR_TA;
typedef struct /* Variablenfeld1 (length=16+26=42) */
{ SR_RBH rbh; /* Header */
BYTE conn_iso_reason_code;
BYTE conn_rfu[4];
WORD conn_ack_delay_estimate;
SR_TA *conn_ta;
WORD conn_persistence_count;
WORD conn_abort_timeout;
WORD conn_reference;
BYTE conn_class;
WORD conn_negot_options;
BYTE *conn_buffer_ptr;
WORD conn_buffer_length;
} SR_VAR_ARRAY_1;
typedef struct /* Variablenfeld2 (length=16+34=50) */
{ SR_RBH rbh; /* Header */
BYTE vc_iso_reason_code;
BYTE vc_rfu[15];
WORD vc_reference;
BYTE vc_conn_class;
WORD vc_buf_len;
BYTE vc_num_blks;
BYTE *vc_buf0_ptr;
WORD vc_buf0_length;
BYTE *vc_buf1_ptr;
WORD vc_buf1_length;
} SR_VAR_ARRAY_2;
typedef struct /* Datagram functions of iNA 960 (length=16+18=34)*/
{ SR_RBH rbh; /* Header */
BYTE dg_rfu[4];
SR_TA *dg_ta;
BYTE dg_tsap_class;
WORD dg_buf_len;
BYTE dg_num_blks;
BYTE *dg_buf0_ptr;
WORD dg_buf0_length;
} SR_DG;
typedef struct /* Setup (length=16+32=48)*/
{ SR_RBH rbh; /* Header */
WORD count;
BYTE address[6];
BYTE usertext[24];
} SR_IA;
typedef struct /* RB-struct*/
{
union
{ SR_OPEN_REQ open;
SR_VAR_ARRAY_1 va1;
SR_VAR_ARRAY_2 va2;
SR_DG dg;
SR_IA setup;
} rb;
} RB1;
#pragma pack(pop, _SR_RB_alignment)
Jetzt habe ich diese Struktur folgendermaßen in C# übernommen:
#region Requestblock structures
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct SR_RBH /* RB-Header (length=16) */
{
public uint rb_res;
public byte rb_length;
public ushort rb_user;
public byte rb_resp_port;
public uint rb_callback;
public byte rb_susys;
public byte rb_opcode;
public ushort rb_response;
}
// #define SR_RB SR_RBH
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct SR_OPEN_REQ /* (length=16+2=18) */
{
public SR_RBH rbh; /* Header */
public ushort open_reference; /* Identifikation */
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct SR_TA /* (length=31) */
{
public byte loc_nsap_id_len;
public byte loc_tsap_id_len;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
public byte[] loc_tsap_id; // = new byte[8];
public byte rem_net_addr_len;
public byte afi;
public ushort subnet;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=6)]
public byte[] host_id; // = new byte[6];
public byte lsap_id;
public byte rem_net_nsap;
public byte rem_tsap_id_len;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
public byte[] rem_tsap_id; // = new byte[8];
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct SR_VAR_ARRAY_1 /* Variablenfeld1 (length=16+26=42) */
{
public SR_RBH rbh; /* Header */
public byte conn_iso_reason_code;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public byte[] conn_rfu; // = new byte[4];
public ushort conn_ack_delay_estimate;
unsafe public SR_TA *conn_ta;
public ushort conn_persistence_count;
public ushort conn_abort_timeout;
public ushort conn_reference;
public byte conn_class;
public ushort conn_negot_options;
unsafe public byte *conn_buffer_ptr;
public ushort conn_buffer_length;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct SR_VAR_ARRAY_2 /* Variablenfeld2 (length=16+34=50) */
{
public SR_RBH rbh; /* Header */
public byte vc_iso_reason_code;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=15)]
public byte[] vc_rfu; // = new byte[15];
public ushort vc_reference;
public byte vc_conn_class;
public byte vc_buf_len;
public byte vc_num_blks;
unsafe public byte *vc_buf0_ptr;
public ushort vc_buf0_length;
unsafe public byte *vc_buf1_ptr;
public ushort vc_buf1_length;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct SR_DG /* Datagram functions of iNA 960 (length=16+18=34)*/
{
public SR_RBH rbh; /* Header */
[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
public byte[] dg_rfu; // = new byte[4];
unsafe public SR_TA *dg_ta;
public byte dg_tsap_class;
public ushort dg_buf_len;
public byte dg_num_blks;
unsafe public byte *dg_buf0_ptr;
public ushort dg_buf0_length;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct SR_IA /* Setup (length=16+32=48)*/
{
public SR_RBH rbh; /* Header */
public ushort count;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=6)]
public byte[] address; // = new byte[6];
[MarshalAs(UnmanagedType.ByValArray, SizeConst=24)]
public byte[] usertext; // = new byte[24];
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct RB /* RB-struct*/
{
public SR_OPEN_REQ open;
public SR_VAR_ARRAY_1 va1;
public SR_VAR_ARRAY_2 va2;
public SR_DG dg;
public SR_IA setup;
}
#endregion Requestblock Structures
Der Aufruf der SRMD_Request Funktion passiert in dem C-Beispiel in folgender Methode:
C-Code:
WORD SRMD_OpenReq(int hLogDev,WORD userid, RB1 *rb)
{
short ret=0;
/* RB1-Header */
rb->rb.open.rbh.rb_res = 0xFFAAFFAA;
rb->rb.open.rbh.rb_length = 18;
rb->rb.open.rbh.rb_user = userid;
rb->rb.open.rbh.rb_resp_port= 0xFF;
rb->rb.open.rbh.rb_callback = 0;
rb->rb.open.rbh.rb_susys = SUBSYS_TCL;
rb->rb.open.rbh.rb_opcode = OPEN_REQ;
rb->rb.open.rbh.rb_response = REQ_WAITING;
/* SRMD_Request */
if(gLoadDLLDynamic == TRUE)
{ ret = pSRMD_Request(hLogDev,rb); }
else
{ ret = SRMD_Request(hLogDev,rb); }
if((ret != 0) || (SR_async) )
{ return (ret); }
ret = SRMD_WaitOK(&rb->rb.open.rbh.rb_response,MAX_POLL_TIME_AKTIV);
if(ret >= 0)
{ ret = rb->rb.open.open_reference; }
return(ret);
}
Diese Funktion habe ich in C# abgebildet, wobei ich die jeweiligen Werte durch debuggen des C-Code herausgefunden und dann hart codiert habe:
private int Open_Req()
{
int ret=-99;
RB rb = new RB();
/* RB1-Header */
rb.open.rbh.rb_res = 0xFFAAFFAA;
rb.open.rbh.rb_length = 18;
rb.open.rbh.rb_user = 0x0000;//userid;
rb.open.rbh.rb_resp_port= 0xFF;
rb.open.rbh.rb_callback = 0;
rb.open.rbh.rb_susys = Kernel.ISO_LAYER_4;
rb.open.rbh.rb_opcode = Kernel.OPEN_REQ;
rb.open.rbh.rb_response = Kernel.REQ_WAITING;
try
{
int i = Init();
ret = SRMD_Request(m_hSRAccessPoint,ref rb);
int error = Marshal.GetLastWin32Error();
Console.WriteLine(message.ToString());
}
catch (Exception ex)
{
int error = Marshal.GetLastWin32Error();
Console.WriteLine("-- EXCEPTION -- " + ex.Message + "-- /EXCEPTION -- ");
Console.WriteLine("-- ERROR -- " + error.ToString() + "-- /ERROR --");
}
return ret;
}
Bei dem Aufruf der SRMD_Request Funktion erhalte ich immer einen Fehlercode und zwar Falscher rb-Parameter.
Jetzt habe ich keine Möglichkeit, herauszufinden, wo mein Fehler liegt, da ich die DLL ja nicht durchdebuggen kann.
Es gibt mehrer Möglichkeiten:
Wenn ich den DllImport aufruf beispielsweise bewusst falsch mache, quasi so:
private static extern int SRMD_Request(int hSRAccessPoint, string rb);
erhalte ich auch die gleiche Fehler Meldung, also Falscher rb-Parameter.
Ich weiß, alles sehr speziell und komplex, doch vielleicht hat jemand hier eine Idee, wie ich das mit Zeigern, -> Operatoren und eigenen Strukturen am besten in C# umsetzen kann.
Vielen Dank schon mal 👍
Beauty is in the eye of the BEER Holder!
Auf den ersten Blick würde ich sagen, dass du das Union-Konstrukt nicht richtig umgesetzt hast. RB ist ein Struct, welches verschiedene Ausprägungen annehmen kann. Bei deiner C#-Implementierung von RB hast du aber alles Varianten zugleich innerhalb des RB-Structs.
Versuch das mal so:
[StructLayout(LayoutKind.Explicit , CharSet=CharSet.Ansi)]
public struct RB /* RB-struct*/
{
[ FieldOffset( 0 )]
public SR_OPEN_REQ open;
[ FieldOffset( 0 )]
public SR_VAR_ARRAY_1 va1;
[ FieldOffset( 0 )]
public SR_VAR_ARRAY_2 va2;
[ FieldOffset( 0 )]
public SR_DG dg;
[ FieldOffset( 0 )]
public SR_IA setup;
}
Die unsafe-Pointer sehen auch gefährlich aus (hier würde ich einfach IntPtr verwenden), aber erstmal den ersten Schritt.
Wenn ich StructLayout(LayoutKind.Explicit und _ [ FieldOffset( 0 )]_ habe kommt folgende Exception.Message
ex.Message
"Der Typ 'Connect_SPS_Test.RB' der Assembly 'Connect_SPS_Test, Version=1.0.2285.19282, Culture=neutral, PublicKeyToken=null' konnte nicht geladen werden, da sie bei Offset 0 ein Objektfeld enthält, das falsch ausgerichtet ist oder von einem nicht-Objektfeld überlappt wird"
Hört sich irgendwie nach einem Problem im Speicherbereich an...
Diese Exception kommt noch bevor mein private int Open_Req() aufgerufen wird... ich kann nicht mal reindebuggen, er wirft sofort die Exception
private void bt_open_Click(object sender, System.EventArgs e)
{
try
{
Open_Req();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Beauty is in the eye of the BEER Holder!
Ok, der von mir vorgeschlagene Weg funktioniert nur mit ValueTypes. Vermutlich ist das "ref" der Killer.
Die Alternative ist folgende: Lege für jeden Union-Member eine eigene Struktur an und erzeuge eine entsprechende DLLImport-Überladung:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct RB_SR_OPEN_REQ /* RB-struct*/
{
public SR_OPEN_REQ open;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct RB_SR_VAR_ARRAY_1 /* RB-struct*/
{
public SR_VAR_ARRAY_1 va1;
}
und
[DllImport("s7_sr.dll", SetLastError=true, CharSet = CharSet.Auto)]
private static extern int SRMD_Request(int hSRAccessPoint, ref RB_SR_OPEN_REQ rb);
[DllImport("s7_sr.dll", SetLastError=true, CharSet = CharSet.Auto)]
private static extern int SRMD_Request(int hSRAccessPoint, ref RB_SR_VAR_ARRAY_1 rb);
usw.
Kannst nur beten, dass du keine Union innerhalb der Union hast, dann wird es sehr unangenehm. Zum Glück bei dir nicht der Fall.
P.s.: Du verdienst den mycsharp-Preis für das bisher komplexeste PInvoke-Problem! 😉