5
$\begingroup$

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!

$\endgroup$
3
  • $\begingroup$ Possibly related MathGroups thread: calling a dll-function that returns a structure. $\endgroup$ Commented Dec 31, 2013 at 13:52
  • $\begingroup$ It's not clear that your last line of code, which splits up the bytes of the structure, is correct. It would be helpful if you could show the actual bytes, meaning the output from Take[bytes, 148]. Also, I think it should be System.Int16, not System.Byte, for the last argument type in your DefineDLLFunction call. $\endgroup$ Commented Dec 31, 2013 at 18:13
  • $\begingroup$ ROSU: please do not use answers for continuations of a question; instead, edit the question with the additional information. Thanks. $\endgroup$
    – Mr.Wizard
    Commented Jan 17, 2014 at 15:28

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.