my struct looks like this in c code:
typedef struct _REQUEST {
char D [_D_MAX+1];
char M [_M_MAX+1];
char T [_T_MAX+1];
char ClRef [_CL_REF_MAX+1];
char load [_LOAD_MAX];
ULONG loadLen;
} _REQUEST, *LP_REQUEST;
in c# like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct _REQUEST_STRUCT {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string D;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string M;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string T;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string clRef;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1200)]
public string Load;
public UInt32 LoadLen;
}
the method I am calling is like this in c:
int
AJ_STDCALL
SimpTran (const char* svc_name,
const LP_REQUEST query,
LP_RESPONSE response)
{
//init stuff
//Validate input parameters
if ( query == NULL )
return(ERR_QUERY_NULL);
if (query->loadLen == 0)
return (ERR_QUERY_NULL);
}
in c#:
[DllImport(LINUXLIB, CallingConvention = CallingConvention.StdCall)]
public static extern int SimpTran(string serID, ref _REQUEST_STRUCT request, out IntPtr response);
and i am calling it like this:
p_request_struct = new _REQUEST_STRUCT();
// fill p_request_struct
// response
p_response_struct = new _RESPONSE_STRUCT();
// initialize unmanaged memory to hold return struct
IntPtr ptrRet = Marshal.AllocHGlobal(Marshal.SizeOf(p_response_struct));
Marshal.StructureToPtr(p_response_struct, ptrRet, false);
iRet = SimpTran(SerID, ref p_request_struct, out ptrRet);
I am getting null query! In C code query==null is true and it returns null. But I am populating the request struct?
Do I have to allocate memory for that as well?
Related
I am trying to convert c++ api example to c#. but I can not find the way of struct pointer in struct by the way struct has string types. Here is c++ structs and functions
struct AuthParam { char server_ip[32]; char username[50]; char password[50]; };
struct CameraInfo { int index; char devicename[100]; char smallrtsp[1000]; char bigrtsp[1000]; };
struct SingleDevice { char deviceid[50]; char devicename[100]; int flag_onuse; int cameralist_size; CameraInfo* cameralist; };
struct DeviceList { int listsize; SingleDevice* singledevicelist; };
typedef int (WINAPI capi_init)(void);
typedef int (WINAPI capi_disabled)(void);
typedef int (WINAPI capi_GetServerTimeCode)(char* server_ip, unsigned int* timecode);
typedef int (WINAPI GetDevicelist)(AuthParam auth_para, DeviceList* devicelist);
and this is my c# code but I can not find the way of defining struct pointer in struct I got "
Error CS0208 Cannot take the address of, get the size of, or declare a pointer to a managed type ('Form1.CameraInfo')" error.
public struct AuthParam
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string server_ip;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string username;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string password;
};
[StructLayout(LayoutKind.Sequential)]
public struct CameraInfo
{
public int index;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string devicename;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1000)]
public string smallrtsp;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1000)]
public string bigrtsp;
};
[StructLayout(LayoutKind.Sequential)]
public struct SingleDevice
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string deviceid;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string devicename;
public int flag_onuse;
public int cameralist_size;
public CameraInfo *cameralist;
};
[StructLayout(LayoutKind.Sequential)]
public unsafe struct DeviceList
{
public int listsize;
public SingleDevice *singledevicelist;
};
[DllImport("c:\\lib\\api_client.dll")] public static extern int capi_init();
[DllImport("c:\\lib\\api_client.dll")] public static extern int capi_disabled();
[DllImport("c:\\lib\\api_client.dll")] public static extern int capi_GetServerTimeCode(string ip, ref uint timecode);
[DllImport("c:\\lib\\api_client.dll")] public static extern int GetDevicelist (AuthParam auth_para,ref DeviceList devicelist);
is there any way to achieve this convertion?
i think this error because of the different nature of c++ (un-managed code) and c# (managed code), so maybe using System.IntPtr as a pointer to your camera struct.
please consider this question here, and you can find about P/Invoke Interop Assistant which is an open source tool to convert your code from the un-managed code to a managed C# code. with a small blog article about C++/C# interoperability
I'm invoking a C++ function from within C#.
The function I am trying to invoke returns following C++ struct type result:
LPWFSCIMNOTETYPELIST lpNoteTypeList;
typedef struct _wfs_cim_note_type_list
{
USHORT usNumOfNoteTypes;
LPWFSCIMNOTETYPE *lppNoteTypes;
} WFSCIMNOTETYPELIST, *LPWFSCIMNOTETYPELIST;
typedef struct _wfs_cim_note_type
{
USHORT usNoteID;
CHAR cCurrencyID[3];
ULONG ulValues;
USHORT usRelease;
BOOL bConfigured;
} WFSCIMNOTETYPE, *LPWFSCIMNOTETYPE;
I found the suitable struct in C#:
[StructLayout(LayoutKind.Sequential, Pack = XFSConstants.STRUCTPACKSIZE, CharSet = CharSet.Ansi)]
public unsafe struct WfsCimNoteType
{
public ushort usNoteID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public char[] cCurrencyID;
public uint ulValues;
public ushort usRelease;
public byte bConfigured;
}
[StructLayout(LayoutKind.Sequential, Pack = XFSConstants.STRUCTPACKSIZE, CharSet = CharSet.Ansi)]
public unsafe struct WfsCimNoteTypeList
{
public ushort usNumOfNoteTypes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public WfsCimNoteType[] lppNoteTypes;
}
I used the following code to parse the result from invoked function:
WfsCimNoteTypeList data = new WfsCimNoteTypeList();
XFSUtil.PtrToStructure<WfsCimNoteTypeList>(lpBuffer, ref data);
(here lbBuffer is IntPtr)
XFSUtil.PtrToStructure is defined like this:
public static void PtrToStructure<T>(IntPtr ptr, ref T p) where T : struct
{
p = (T)Marshal.PtrToStructure(ptr, typeof(T));
}
But after parsing from IntPtr to WfsCimNoteType object array, I am getting different values???
My C++ dll has two struct.
C++:
typedef struct interface_info {
DWORD index;
DWORD ipv4Addr;
BYTE macAddr[6];
DWORD gateway;
}I_INFO;
typedef struct user_info {
DWORD uniqueid;
BYTE userid[32];
BYTE pwd[32];
BYTE info[5][64];
}U_INFO;
C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct I_INFO
{
public uint Index;
public uint ipv4Addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] macAddr;
public uint gateway;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct U_INFO
{
public uint uniqueid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] userid;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] pwd;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 320)]
public byte[] info;
};
I'm calling this method:
C++:
void GetNetworkInfo(I_INFO *n); // Show interface Info
dword RegNewUser(U_INFO *u); // Save to test.ini
C#:
[DllImport("TestNet.dll")]
public static extern void GetNetworkInfo(ref I_INFO n);
[DllImport("TestNet.dll")]
public static extern uint RegNewUser(ref U_INFO u);
I'm calling this way:
C#:
I_INFO ii = new I_INFO();
GetNetworkInfo(ref ii);
MessageBox.Show(ii.index+ "\n"
+ Long2ip(ii.ipAddr) + "\n"
+ Long2ip(ii.gateway) + "\n"
+ Bytes2Str(ii.macAddr);
....
I_INFOuser = new I_INFO
{
uid = Encoding.ASCII.GetBytes("testUser"),
pwd = Encoding.ASCII.GetBytes("testPassword"),
info = Encoding.ASCII.GetBytes("hello world!")
};
try
{
RegNewUser(ref user);
}
catch(Exception err)
{
MessageBox.Show(err.ToString());
}
The output is:
GetNetworkInfo returns normal results, but RegNewUser outputs this error:
Error: Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout.
How do I fix this?
I'm working with SafeCom (print server) and figured out that they have a Administrator DLL Programmer’s Manual. As a proof of concept I wanted to make a small C# application that sends and retrieves data using this library. I've managed to insert a user object by calling LoginByLogon() followed by AddUser().
However, I'm not able to retrieve/return a user struct by calling LoginByLogon() followed by GetUser. I can see that ptrTemp is set (not null), but all properties in newStructure are still null. I don't fully understand PInvoke yet, so I hope someone here can enlighten me a little. :)
I'm for now importing the following:
[DllImport("scAPI.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int sc_LoginByLogon(string szIpAddr, string szUserName, string szPassword);
[DllImport("scAPI.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int sc_Logoff();
[DllImport("scAPI.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern int sc_GetUserInfoByUserLogon(string szUserLogon, out IntPtr ppUserInfo);
[DllImport("scAPI.dll", CallingConvention = CallingConvention.Cdecl)]
static extern short sc_AddUser(ref sUserInfoApiV4 ppUserInfo, double fAccount, double FLowLimit, ref int pNewId);
Define my structs:
struct SYSTEMTIME
{
short wYear;
short wMonth;
// short wDayOfWeek;
short wDay;
short wHour;
short wMinute;
short wSecond;
short wMilliseconds;
public static implicit operator SYSTEMTIME(DateTime time)
{
return new SYSTEMTIME
{
wYear = (short)time.Year,
wMonth = (short)time.Month,
wDay = (short)time.Day,
// wDayOfWeek = (short)time.DayOfWeek,
wHour = (short)time.Hour,
wMinute = (short)time.Minute,
wSecond = (short)time.Second,
wMilliseconds = (short)time.Millisecond
};
}
public static implicit operator DateTime(SYSTEMTIME time)
{
return new DateTime(time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds);
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Card
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string m_szCardNo; //Card number
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
public string m_szPINCode; //PIN code - zero terminated
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string m_szPUKCode; //PUK code - zero terminated
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
public struct sUserInfoApiV4
{
[MarshalAs(UnmanagedType.I4)]
public Int32 m_nStructLength; //Length of the struct including this field
[MarshalAs(UnmanagedType.I2)]
public short m_nVersion; //Version Number (set to 4)
[MarshalAs(UnmanagedType.I2)]
public short m_nSubVersion; //Version of Reserved (set to 0)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] m_achReserved; // Get room for m_nAccessRights
[MarshalAs(UnmanagedType.I2)]
public short m_nUserType; //USER_STRUCT (uses as UserType - BitFields)
[MarshalAs(UnmanagedType.I4)]
public int m_nUserId; //Unique id identifying the user
public int m_nSubRights; // Sub version 2
[MarshalAs(UnmanagedType.I4)]
public int m_nNotUsed1; // Sub version 5
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 101)]
public string m_wzFullName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 101)]
public string m_wzDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 101)]
public string m_wzEMail;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
public string m_wzUserLogon; //Asc-ii string identifying the user
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
public string m_wzPassword; //Asc-ii string password
public Card card;
[MarshalAs(UnmanagedType.I2)]
public short m_nLogonFails; //Number of failed logins
[MarshalAs(UnmanagedType.I2)]
public short m_bUserLocked; //Unlocked(0), Locked(1)
[MarshalAs(UnmanagedType.I2)]
public short m_bUserDisabled; //Not Disable(0), Open(1)
[MarshalAs(UnmanagedType.I2)]
public short m_bAvoidPin; //Pin Enabled(0), Pin disabled (1)
[MarshalAs(UnmanagedType.I2)]
public short m_bPrintAll; //Print All when card is slid - Yes(1) No(0)
[MarshalAs(UnmanagedType.I2)]
public short m_bCardOpen; //PIN code assigned(0), Awaiting pin assignment/Outstanding PUK(1)
[MarshalAs(UnmanagedType.I2)]
public short m_nBillingModel; //None(0), BillingDialog(1)
[MarshalAs(UnmanagedType.I2)]
public short m_nAccountingModel; //None(0), Tracking(1), Pay(2)
[MarshalAs(UnmanagedType.I4)]
public int m_nUserRights; // None(0)
[MarshalAs(UnmanagedType.I2)]
public short m_bAllowEncryption; // The user uses encryption YES(1) NO(0)
[MarshalAs(UnmanagedType.U1)]
bool m_bAllowCheckPrinting; // The user is allowed to receives checks YES(1) NO(0)
[MarshalAs(UnmanagedType.U1)]
bool m_bAllowPmail; // The user is allowed distribution YES(1) NO(0)
[MarshalAs(UnmanagedType.U1)]
bool m_bDenyRetain; // User allowed to retain his documents. New in SubVersion 3
public int m_lCreationDate;
public int m_lLastLogin;
[MarshalAs(UnmanagedType.I4)]
public int m_nServerId; // HomeServer of this user.
[MarshalAs(UnmanagedType.I4)]
public int m_nDomainId; // New in Subversion 1 - Reference to the windows domain the user are located in
[MarshalAs(UnmanagedType.I4)]
public int m_nTreeNodeId;
[MarshalAs(UnmanagedType.I4)]
public int m_nNid;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 51)]
public string m_wzCostCode; // Cost center, new in SubVersion 3
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct SCardInfoApiV4
{
[MarshalAs(UnmanagedType.I4)]
public Int32 m_nStructLength; // Length of entire struct
[MarshalAs(UnmanagedType.I2)]
public short m_nVersion; // Version (short)
[MarshalAs(UnmanagedType.I2)]
public short m_nSubVersion; // Subversion of reserved, set to 1 (short)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] m_achReserved; // Reserved for protocol expansion (fixed char)
[MarshalAs(UnmanagedType.I4)]
public int m_nCardId;
[MarshalAs(UnmanagedType.I4)]
public int m_nUserId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string m_achCardNo; // originalt fixed char [40]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
public string m_achReservedPinCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string m_achReservedPukCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 51)]
public string m_achDescription;
[MarshalAs(UnmanagedType.I4)]
int m_nCreatorId;
SYSTEMTIME m_sCreated;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
public string m_nUseLifeTime; //originalt char
SYSTEMTIME m_sLifeStart;
SYSTEMTIME m_sLifeEnd;
[MarshalAs(UnmanagedType.I4)]
int m_nSourceId; // Sub version 1
}
And finally my methods:
public int LoginByLogon(string ipAddr, string userLogon, string password)
{
return sc_LoginByLogon(ipAddr, userLogon, password);
}
public int Logoff()
{
return sc_Logoff();
}
public sUserInfoApiV4 GetUser(string userLogon)
{
sUserInfoApiV4 tmpUserInfo = new sUserInfoApiV4();
IntPtr ptrTemp = Marshal.AllocHGlobal(Marshal.SizeOf(tmpUserInfo));
sc_GetUserInfoByUserLogon(userLogon, out ptrTemp);
Marshal.StructureToPtr(tmpUserInfo, ptrTemp, false);
sUserInfoApiV4 newStructure;
newStructure = (sUserInfoApiV4)Marshal.PtrToStructure(ptrTemp, typeof(sUserInfoApiV4));
return newStructure;
}
public unsafe int AddUser(string usercode, string email, string pinCode, string fullName)
{
sUserInfoApiV4 sUserstruct = new sUserInfoApiV4();
int sizeOfstruct = Marshal.SizeOf(typeof(sUserInfoApiV4));
IntPtr pSUserstruct = Marshal.AllocHGlobal(sizeOfstruct); //Make note of memory used
Marshal.StructureToPtr(sUserstruct, pSUserstruct, false);
sUserstruct.m_wzUserLogon = usercode;
sUserstruct.card.m_szCardNo = usercode; //Card number same as logon
sUserstruct.card.m_szPINCode = pinCode;
sUserstruct.m_wzFullName = fullName;
sUserstruct.m_wzEMail = email;
sUserstruct.m_nUserRights = 3; //Standard user rights
sUserstruct.m_nAccountingModel = 2; //Pay user
sUserstruct.m_nVersion = 4; //Safecom version
sUserstruct.m_nStructLength = sizeOfstruct;
sUserstruct.m_bAvoidPin = 1; // Disable PIN
int userId = 0; //Value of variable not used, just to initialize the variable. Used as pointer for return value. Index number for user in db
int* userIndex = &userId;
int nRes = sc_AddUser(ref sUserstruct, 0, 0, ref *userIndex);
Marshal.FreeHGlobal(pSUserstruct); // Release memory
pSUserstruct = IntPtr.Zero; // used by pointer.
return nRes;
}
C++
#define FIELD_SIZE_MSGID 24
#define FIELD_SIZE_TIME 12
#define FIELD_SIZE_ADMIN 256
typedef struct
{
char MsgId[FIELD_SIZE_MSGID+1];
char SendTime[FIELD_SIZE_TIME+1];
char ReceiptTime[FIELD_SIZE_TIME+1];
} AdminDataM0;
typedef struct
{
int Type;
union
{
AdminDataM0 M0;
char Data[FIELD_SIZE_ADMIN + 1];
} AdData;
char Unknown[FIELD_SIZE_ADMIN + 1];
} AdminData;
C#:
[DllImport("Receiver.dll",
CallingConvention = CallingConvention.Cdecl,
ExactSpelling = false,
SetLastError = false,
CharSet = CharSet.Ansi,
EntryPoint = "SendMessage")]
[return: MarshalAs(UnmanagedType.I4)]
protected static extern int SendMessage(
[MarshalAs(UnmanagedType.Struct)] ref AdminData ptrAdminData,
);
protected const Int32 FIELD_SIZE_MSGID = 24;
protected const Int32 FIELD_SIZE_TIME = 12;
protected const Int32 FIELD_SIZE_ADMIN = 256;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct AdminDataM0
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FIELD_SIZE_MSGID + 1)]
public char[] MsgId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FIELD_SIZE_TIME + 1)]
public char[] SendTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FIELD_SIZE_TIME + 1)]
public char[] ReceiptTime;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
protected struct AdminData
{
[MarshalAs(UnmanagedType.I4)]
public Int32 nType;
[MarshalAs(UnmanagedType.Struct)]
public Data AdminData_Data;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct Data
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.Struct)]
public AdminDataM0 M0; //135
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FIELD_SIZE_ADMIN + 1)]
public char[] Data_FldSizeAdmin;
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FIELD_SIZE_ADMIN + 1)]
public char[] Unknown;
}
MAIN:
AdminData oAdminData = new AdminData();
oAdminData.AdminData_Data = new oAdminData.Data();
oAdminData.AdminData_Data.M0 = new oAdminDataM0();
oAdminData.AdminData_Data.M0.MsgId = new char[FIELD_SIZE_MSGID + 1];
oAdminData.AdminData_Data.M0.SendTime = new char[FIELD_SIZE_TIME + 1];
oAdminData.AdminData_Data.M0.ReceiptTime = new char[FIELD_SIZE_TIME + 1];
oAdminData.AdminData_Data.Data_FldSizeAdmin = new char[FIELD_SIZE_ADMIN + 1];
oAdminData.Unknown = new char[FIELD_SIZE_ADMIN + 1];
string M0_MsgId = "MsgId";
string M0_SendTime = "Send Time";
string M0_ReceiptTime = "ReceiptTime";
string unknown = "Unknown";
M0_MsgId.ToCharArray().CopyTo(oAdminData.AdminData_Data.M0.MsgId, 0);
M0_SendTime.ToCharArray().CopyTo(oAdminData.AdminData_Data.M0.SendTime, 0);
M0_ReceiptTime.ToCharArray().CopyTo(oAdminData.AdminData_Data.M0.ReceiptTime, 0);
// function to DLL
SendMessage(ref oAdminData);
Problem:
Only the MsgId and the DataData_FldSizeAdmin have values which is the same value.
I think it's because they are sharing the same memory address.
UNKNOWN, SENDTIME and RECEIPTIME don't have values.
In stead of MarshalAs[UnmanagedType.ByValArray], you should use MashalAs[UnmanagedType.ByValTStr] inside the declaration of your struct:
as in:
...
public struct AdminDataM0
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FIELD_SIZE_MSGID + 1)]
public string MsgId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FIELD_SIZE_TIME + 1)]
public string SendTime;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FIELD_SIZE_TIME + 1)]
public string ReceiptTime;
}
...
See the documentation:
ByValTStr: Used for in-line,
fixed-length character arrays that
appear within a structure. The
character type used with ByValTStr is
determined by the
System.Runtime.InteropServices.CharSet
argument of the
System.Runtime.InteropServices.StructLayoutAttribute
applied to the containing structure.
Always use the
MarshalAsAttribute.SizeConst field
to indicate the size of the array.
.NET Framework ByValTStr types behave
like C-style, fixed-size strings
inside a structure (for example, char
s[5]). The behavior in managed code
differs from the Microsoft Visual
Basic 6.0 behavior, which is not null
terminated (for example, MyString As
String * 5).
The documentation for ´ByValArray´ sais (emphasis mine):
When MarshalAsAttribute.Value is set
to ByValArray, the SizeConst must be
set to indicate the number of elements
in the array. The ArraySubType field
can optionally contain the
UnmanagedType of the array elements
when it is necessary to differentiate
among string types. You can only use
this UnmanagedType on an array that
appear as fields in a structure.
So I think for your code to work using ByValArray, you should also add a ArraySubType of LPStr to the MarshalAs attribute.