I've been struggling on how to migrate this VB6 code into C#. It involves calling a function inside a DLL passing an array of structure, among other things.
So in VB6, the "struct" declaration is like this:
'Define structure for RGETDAT_STR procedure call
Type rgetdat_str_data_str
type As Integer 'data type (set internally)
file As Integer 'file in database
rec As Integer 'record in file
word As Integer 'word offset in record
start_bit As Integer 'UNUSED
length As Integer 'length of string
flags As Integer 'flags
padding1 As Integer 'UNUSED
value As String 'database value
status As Integer 'return status
padding2 As Integer 'UNUSED
End Type
and one function that uses this "struct" has a method declared as this:
Public Declare Function rgetdat_str Lib "hscnetapi.dll" _
Alias "rgetdat_str_vb" _
(ByVal Server As String, ByVal num_points As Integer, _
getdat_str_data() As rgetdat_str_data_str) As Integer
So, I attempted to convert these 2 pieces of code into C#. I had tried so many variations, but I will post here the latest one that I have. The idea is to call the function via P/Invoke.
The C# struct (so far):
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct rgetdat_str_data_str
{
public short type;
public short file;
public short rec;
public short word;
public short start_bit;
public short length;
public short flags;
public short padding1;
[MarshalAs(UnmanagedType.LPStr)]
public string value;
public short status;
public short padding2;
}
and the function import (so far):
[DllImport("hscnetapi.dll", EntryPoint = "rgetdat_str_vb")]
public static extern short rgetdat_str(
[MarshalAs(UnmanagedType.LPTStr)]
string Server,
short num_points,
[In,Out, MarshalAs(UnmanagedType.LPArray)]
ref rgetdat_str_data_str[] getdat_str_data);
Nothing worked so far in my various experiments on marshalling attributes with the parameters.
I managed to find the C header file for this DLL, and the declaration looks like this:
EXTERN_C short __loadds CALLBACK rgetdat_str_vb_ansi
_DECLARE((char *szHostname, short cRequests, SAFEARRAY **ppsa));
and the "struct" in the C world is declared like this:
/* define union used in rgetdat_value in RGETDAT procedure call */
typedef union rgetdat_value_str
{
n_short int2;
n_long int4;
n_float real4;
n_double real8;
n_char *str;
n_ushort bits;
} rgetdat_value;
/* define structure for RGETDAT procedure call */
typedef struct rgetdat_data_str
{
n_ushort type;
n_ushort file;
n_ushort rec;
n_ushort word;
n_ushort start_bit;
n_ushort length;
n_short flags;
rgetdat_value value;
n_short status;
} rgetdat_data;
In my frustration, I tried to open this DLL with the ITypeLib Viewer tool. I was surprised that the DLL file can be opened, even though I cannot add this DLL as a Reference in my project. Anyway, a couple of things that I observed within the viewer.
The function has this signature:
[entry("rgetdat_str_vb"), helpstring("...")]
short _stdcall rGetdat_Str(
[in] LPSTR Server,
[in] short num_points,
[in, out] SAFEARRAY(rGetdat_Str_Data_Str)* getdat_str_data);
and the "struct" looked like this:
typedef struct tagrGetdat_Str_Data_Str {
short type;
short file;
short rec;
short word;
short start_bit;
short length;
short flags;
short padding1;
BSTR value;
short status;
short padding2;
} rGetdat_Str_Data_Str;
Based on these observations, I played around with the marshalling attributes of the C# struct, for example,
1.) Changing the struct's value attribute to [MarshalAs(UnmanagedType.BStr)]
2.) Changing the function's getdat_str_data parameter attribute to MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_RECORD)
and still nothing works.
There's an blog/article talking about a similar topic here: http://limbioliong.wordpress.com/2012/02/28/marshaling-a-safearray-of-managed-structures-by-pinvoke-part-1/ but I can't just wrap my head around it.
It seems that VB6 can do it very simply compared to C# (.Net) with this DLL function call. Any hints or ideas out there on how to DLLImport declare this function in C# (.Net)?