I want to use my Bluechip PCI-ADC acquisition card in Mathematica via his dynamic link library “Bcdll32.dll”; (used to access functions on my PCI data acquisition cards). My problem is the function “BCTGetBoardId” that will open a Board by providing a board type (integer 19 to my card) and a board number (integer 0 to my card). By considering VB.Net programming language, the BCTGetBoardId function is defined as:
Public Declare Function BCTGetBoardId Lib "BCDLL32.DLL"
(ByRef pBoardID As BCT_BOARD_ID, ByVal nDevType As Short, ByVal nBoard As Short) As Integer
where the variable pBoardID is a BCT_BOARD_ID type; in VB.Net this type is defined as:
<StructLayout(LayoutKind.Sequential)> _
Public Structure BCT_BOARD_ID
Dim Handle As Integer
Dim SerialNo As Integer
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=30, ArraySubType:=UnmanagedType.AsAny)> _
Dim DevName() As Byte
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=30, ArraySubType:=UnmanagedType.AsAny)> _
Dim Clock0Name() As Byte
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=30, ArraySubType:=UnmanagedType.AsAny)> _
Dim Clock1Name() As Byte
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=30, ArraySubType:=UnmanagedType.AsAny)> _
Dim Clock2Name() As Byte
Dim hClock0 As BCT_HANDLE
Dim hClock1 As BCT_HANDLE
Dim hClock2 As BCT_HANDLE
Dim Index As Integer
Dim DeviceType As Integer
End Structure
According the “BLUECHIP.vb” file that allows access to DLL library functions IN VB.Net programs, in the case of the BCT_BOARD_ID structure described above, ” In VB.NET, we cannot declare a dimensioned array from within the structure. Instead, we use the 'MarshalAs Attribute' class to handle each array data.” To obtain the data contained in pBoardID variable and by using the help offered by (How can multiple null terminated strings be handled in a DLL-function result?) I used a Mathematica program as follows:
Needs["NETLink`"]
path="c:\\WINDOWS\\system32\\Bcdll32.dll"; (*my Bcdll32.dll path*)
codgbi=DefineDLLFunction["BCTGetBoardId",path,"short",{"IntPtr","System.Int16","System.Byte"}];
$maxBufLen=1000;
LoadNETType["System.Runtime.InteropServices.Marshal"];
buf=Marshal`AllocHGlobal[$maxBufLen];
codergbi=codgbi[buf,19,0]
managedArray=NETNew["System.Byte[]",$maxBufLen];
Marshal`Copy[buf,managedArray,0,$maxBufLen];
bytes=NETObjectToExpression[managedArray];
FromCharacterCode[DeleteCases[SplitBy[bytes, # != 0 &], {0 ..}]]
I obtained the following confuse answer:
Out[18] 0 (*the value codergbi=0 indicate a correct function syntax *)
Out[22] {"à.02", "D0.10", "\\.\PCIADC0_0000", \"4B-B1DA-6F7CA\\.\PCIADC0_5400", "\\.\PCIADC0_5401", \"\\.\PCIADC0_5402", "+", "Realtek PCIe GBE Family Controller - \Packet Scheduler Miniport", ".06", ".08`no.b4", ".02", ".06", \"", "192.168.1.120", "255.255.255.0", ".02", "192.168.1.1", \"0.0.0.0", "192.168.1.1", "0.0.0.0", "0.0.0.0", "0.0.0.0", "0.0.0.0", \"ÞÂR^ÝÃR"}
It is clear that I am not mastered the subtleties of programming in NET. Framework. Please, any help to extract in Mathematica all the correct form of the data contained in the structure “buf” from the function codgbi[buf,19,0]!
First of all, many thanks Alexey Popkov for your sugestion “calling a dll-function that returns a structure and many, many thanks Todd Gayley, for the superb examples presented in the same MathGroups thread above. Please ignore the last line of my first code, which splits up the bytes of the structure; this was a stupid trial inspired by “How can multiple null terminated strings be handled in a DLL-function result?” and bad adapted by me. To understand how to solve my problem I rebuilt and corrected some small errors (sign == bad placed due to the publisher of the site), the didactic examples offered by Todd Gayley . My second problem is that I deal with a nested structure: the BCT_BOARD_ID structure presented above use the BCT_HANDLE structure and the last use OVERLAPPED structure as follows:
Structure OVERLAPPED
Dim Internal As Integer
Dim InternalHigh As Integer
Dim Offset As Integer
Dim OffsetHigh As Integer
Dim hEvent As Integer
End Structure
Public Structure BCT_HANDLE
Dim Handle As Integer
Dim Overlap As OVERLAPPED
Dim DeviceType As Integer
Dim BoardType As Integer
Dim nPort As Short
Dim LastError As Integer
Dim NextTime As Integer
End Structure
After analyzing the material on nested structures (pinvoke.net and others) I suggested the following DefineDLLFunction:
path = "c:\\WINDOWS\\system32\\Bcdll32.dll";
bctboardid = DefineDLLFunction[
"
public struct OVERLAPPED
{
public System.Int16 Internal;
public System.Int16 InternalHigh;
public System.Int16 Offset;
public System.Int16 OffsetHigh;
public System.Int16 hEvent;
}
Public struct BCT_HANDLE
{
public System.Int16 Handle;
public OVERLAPPED Overlap;
public System.Int16 DeviceType;
public System.Int16 BoardType;
public System.Byte nPort;
public System.Int16 LastError;
public System.Int16 NextTime;
}
[StructLayout(LayoutKind.Sequential)]
public struct BCT_BOARD_ID
{
public System.Int16 Handle;
public System.Int16 SerialNo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=30, ArraySubType=UnmanagedType.AsAny)]
public System.Byte DevName();
[MarshalAs(UnmanagedType.ByValArray, SizeConst=30, ArraySubType=UnmanagedType.AsAny)]
public System.Byte Clock0Name();
[MarshalAs(UnmanagedType.ByValArray, SizeConst=30, ArraySubType=UnmanagedType.AsAny)]
public System.Byte Clock1Name();
[MarshalAs(UnmanagedType.ByValArray, SizeConst=30, ArraySubType=UnmanagedType.AsAny)]
public System.Byte Clock2Name();
public BCT_HANDLE hClock0;
public BCT_HANDLE hClock1;
public BCT_HANDLE hClock2;
public System.Int16 Index;
public System.Int16 DeviceType;
}
[return: MarshalAs(UnmanagedType.Byte)]
[DllImport(path, CallingConvention = CallingConvention.StdCall,CharSet = CharSet.Auto, SetLastError = true)]
public static extern short
BCTGetBoardId([In, Out] BCT_BOARD_ID pBoardId,System.Int16 nDevType,System.Int16 nBoard );"]
Unfortunately I received the message:
DefineDLLFunction::compileerr: "The compiler reported errors in your DLL function declaration: "Invalid token 'struct' in class, struct, or interface member declaration ".
Please, help me identify the error!
Take[bytes, 148]
. Also, I think it should beSystem.Int16
, notSystem.Byte
, for the last argument type in yourDefineDLLFunction
call. $\endgroup$