Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. Visual Basic
  4. Calling from VB.net to a Dll Win32 function that uses safearray pointer

Calling from VB.net to a Dll Win32 function that uses safearray pointer

Scheduled Pinned Locked Moved Visual Basic
comquestioncsharpdata-structures
7 Posts 2 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    E Offline
    edmonson
    wrote on last edited by
    #1

    Hi, I'am updating and old VB6 project to VB.net 2008. My old project uses a Win32 Dll and TLB file to shared types. From VB6 a reference is created to TLB file and interop works fine. From VB.net I can import TLB file successfully and types are accessibles, but not the functions. Using 'Declare Function' sentences to import DLL methods, I can access to DLL functions. My Win32 DLL has methods that uses LPSAFEARRAY pointer as input parameters. This is the declaration of one of this functions on DLL:

    extern NOMANGLE short CCONV EncenderLEDs(LPSAFEARRAY *CBLeds, bool Estado);

    To reference this from VB.net I write

    Public Declare Function EncenderLEDs Lib "MonAuto.dll" (<MarshalAs(UnmanagedType.SafeArray)> ByRef CBLeds() As CBTipo, ByVal Estado As Boolean) As Integer

    CBTIPO is defined on DLL as:

    typedef struct{
    short Carta;
    short Borna;
    } CBTipo;

    The type is correctly visible at VB.net after TLB is imported. I test the function with next code:

    Dim CBLeds() as CBTipo
    Redim CBLeds(4)
    CBLeds(0).Borna = 0
    CBLeds(0).Carta = 0
    EncenderLEDs(CBLeds, False)

    An exception is reported: (Exception of HRESULT: 0x8002802B (TYPE_E_ELEMENTNOTFOUND)) But if I use any single Type to create the array: Int32, Double, Single, byte, ... and modify the declaration function definition on Vb.net like:

    Public Declare Function EncenderLEDs Lib "MonAuto.dll" (<MarshalAs(UnmanagedType.SafeArray)> ByRef CBLeds() As Integer, ByVal Estado As Boolean) As Integer

    and modify the test in the same way: <pre>Dim CBLeds() as Integer Redim CBLeds(4) CBLeds(0).Borna = 0 CBLeds(0).Carta = 0 EncenderLEDs(CBLeds, False)</pre> Then the call is processed good. The question is, How must be defined a DLL Win32 function on VB.net that uses a LPSAFEARRAY * as input parameter, to send a userdefined type array ? I've tried:

    Public Declare Function EncenderLEDs Lib "MonAuto.dll" (<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_USERDEFINED)> ByRef CBLeds() As CBTipo, ByVal Estado As Boolean) As Integer

    and fails too. Regards in advance

    _ 1 Reply Last reply
    0
    • E edmonson

      Hi, I'am updating and old VB6 project to VB.net 2008. My old project uses a Win32 Dll and TLB file to shared types. From VB6 a reference is created to TLB file and interop works fine. From VB.net I can import TLB file successfully and types are accessibles, but not the functions. Using 'Declare Function' sentences to import DLL methods, I can access to DLL functions. My Win32 DLL has methods that uses LPSAFEARRAY pointer as input parameters. This is the declaration of one of this functions on DLL:

      extern NOMANGLE short CCONV EncenderLEDs(LPSAFEARRAY *CBLeds, bool Estado);

      To reference this from VB.net I write

      Public Declare Function EncenderLEDs Lib "MonAuto.dll" (<MarshalAs(UnmanagedType.SafeArray)> ByRef CBLeds() As CBTipo, ByVal Estado As Boolean) As Integer

      CBTIPO is defined on DLL as:

      typedef struct{
      short Carta;
      short Borna;
      } CBTipo;

      The type is correctly visible at VB.net after TLB is imported. I test the function with next code:

      Dim CBLeds() as CBTipo
      Redim CBLeds(4)
      CBLeds(0).Borna = 0
      CBLeds(0).Carta = 0
      EncenderLEDs(CBLeds, False)

      An exception is reported: (Exception of HRESULT: 0x8002802B (TYPE_E_ELEMENTNOTFOUND)) But if I use any single Type to create the array: Int32, Double, Single, byte, ... and modify the declaration function definition on Vb.net like:

      Public Declare Function EncenderLEDs Lib "MonAuto.dll" (<MarshalAs(UnmanagedType.SafeArray)> ByRef CBLeds() As Integer, ByVal Estado As Boolean) As Integer

      and modify the test in the same way: <pre>Dim CBLeds() as Integer Redim CBLeds(4) CBLeds(0).Borna = 0 CBLeds(0).Carta = 0 EncenderLEDs(CBLeds, False)</pre> Then the call is processed good. The question is, How must be defined a DLL Win32 function on VB.net that uses a LPSAFEARRAY * as input parameter, to send a userdefined type array ? I've tried:

      Public Declare Function EncenderLEDs Lib "MonAuto.dll" (<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VarEnum.VT_USERDEFINED)> ByRef CBLeds() As CBTipo, ByVal Estado As Boolean) As Integer

      and fails too. Regards in advance

      _ Offline
      _ Offline
      _Erik_
      wrote on last edited by
      #2

      Very beautiful question.:thumbsup: LPSAFEARRAY is a pointer itself, so LPSAFEARRAY* is a pointer to pointer declaration. This is what I would try:

      Public Declare Function EncenderLEDs Lib "MonAuto.dll" (cbLeds() As IntPtr, estado As Boolean) As Integer)

      Your first array initialization is ok, but in order to pass it to that function, you should first pin the objects in the managed heap, create the IntPtr array with their addresses and pass this array. This is very similar to another question I answered in C# forum[^] some time ago, so let me bring it to you with some of the required changes. It is still C#. I will not convert it to VB becouse I do not feel comfortable enough with this language, and I could end up with a little mess.

      GCHandle[] handleArray = new GCHandle[cbLeds.Length];
      for (int i=0; i<cbLeds.Length; i++)
      handleArray[i] = GCHandle.Alloc(cbLeds[i], GCHandleType.Pinned);

      IntPtr[] pointers = (from GCHandle handle in handleArray select
      handle.GetAddrOfPinnedObject()).ToArray();

      int ret = EncenderLEDs(pointers, false)
      // Release the handles
      foreach (GCHandle handle in handleArray)
      handle.Free();

      Please, let us know if it worked.

      E 1 Reply Last reply
      0
      • _ _Erik_

        Very beautiful question.:thumbsup: LPSAFEARRAY is a pointer itself, so LPSAFEARRAY* is a pointer to pointer declaration. This is what I would try:

        Public Declare Function EncenderLEDs Lib "MonAuto.dll" (cbLeds() As IntPtr, estado As Boolean) As Integer)

        Your first array initialization is ok, but in order to pass it to that function, you should first pin the objects in the managed heap, create the IntPtr array with their addresses and pass this array. This is very similar to another question I answered in C# forum[^] some time ago, so let me bring it to you with some of the required changes. It is still C#. I will not convert it to VB becouse I do not feel comfortable enough with this language, and I could end up with a little mess.

        GCHandle[] handleArray = new GCHandle[cbLeds.Length];
        for (int i=0; i<cbLeds.Length; i++)
        handleArray[i] = GCHandle.Alloc(cbLeds[i], GCHandleType.Pinned);

        IntPtr[] pointers = (from GCHandle handle in handleArray select
        handle.GetAddrOfPinnedObject()).ToArray();

        int ret = EncenderLEDs(pointers, false)
        // Release the handles
        foreach (GCHandle handle in handleArray)
        handle.Free();

        Please, let us know if it worked.

        E Offline
        E Offline
        edmonson
        wrote on last edited by
        #3

        No good result. The translated code to VB is:

        Dim CBLeds(4) As CBTipo

            CBLeds(0).Borna = 0
            CBLeds(0).Carta = 0
        
            Dim handleArray(4) As GCHandle
            For i As Integer = 0 To CBLeds.Length - 1
                handleArray(i) = GCHandle.Alloc(CBLeds(i), GCHandleType.Pinned)
            Next i
            Dim pointers() As IntPtr = (From handle As GCHandle In handleArray Select handle.AddrOfPinnedObject()).ToArray()
            Dim ret As Integer = EncenderLEDs(pointers, False) ' Release the handlesforeach (GCHandle handle in handleArray)    handle.Free();
        
        
            For Each handle As GCHandle In handleArray
                handle.Free()
            Next
        

        When I Call the DLL function, the process operates normally (no exception appears) but DLL function not executes correctly. Below you can see the code at EncenderLeds function. I'm getting the Ubound and Lbound of array to verify that this is passed good. The return of SafeArrayGetLBound is DISP_E_BADINDEX 8002000B The return of SafeArrayGetUBound is DISP_E_BADINDEX 8002000B So execution is failed.

        NOMANGLE short CCONV EncenderLEDs(LPSAFEARRAY *CBLeds, bool Estado)
        {
        CBTipo HUGEP *Leds;
        long LBound, UBound;
        //USHORT MatLeds[128];
        //DWORD ReceivedBytes;
        DWORD BytesWritten;
        DATA Adat[256];
        short ActAdat=0;
        short nLed;
        int j;
        USHORT ErrorCnt;
        char Cadena[200];

        for(unsigned char i=0;i<128;++i)
        {
        	Adat\[i\].Address = 0;
        	Adat\[i\].Data = 0;
        	Adat\[128+i\].Address = 0;
        	Adat\[128+i\].Data = 0;
        }
        
        
        int k,l;
        k=SafeArrayGetLBound(\*CBLeds,1,&LBound);
        l=SafeArrayGetUBound(\*CBLeds,1,&UBound);
        sprintf(Cadena,"Valores %x %x %d %d",k,l,LBound,UBound);
        MessageBox(NULL,Cadena,"Titol",0);
        
            return 0;
        

        }

        Any Idea what is happen?

        _ 1 Reply Last reply
        0
        • E edmonson

          No good result. The translated code to VB is:

          Dim CBLeds(4) As CBTipo

              CBLeds(0).Borna = 0
              CBLeds(0).Carta = 0
          
              Dim handleArray(4) As GCHandle
              For i As Integer = 0 To CBLeds.Length - 1
                  handleArray(i) = GCHandle.Alloc(CBLeds(i), GCHandleType.Pinned)
              Next i
              Dim pointers() As IntPtr = (From handle As GCHandle In handleArray Select handle.AddrOfPinnedObject()).ToArray()
              Dim ret As Integer = EncenderLEDs(pointers, False) ' Release the handlesforeach (GCHandle handle in handleArray)    handle.Free();
          
          
              For Each handle As GCHandle In handleArray
                  handle.Free()
              Next
          

          When I Call the DLL function, the process operates normally (no exception appears) but DLL function not executes correctly. Below you can see the code at EncenderLeds function. I'm getting the Ubound and Lbound of array to verify that this is passed good. The return of SafeArrayGetLBound is DISP_E_BADINDEX 8002000B The return of SafeArrayGetUBound is DISP_E_BADINDEX 8002000B So execution is failed.

          NOMANGLE short CCONV EncenderLEDs(LPSAFEARRAY *CBLeds, bool Estado)
          {
          CBTipo HUGEP *Leds;
          long LBound, UBound;
          //USHORT MatLeds[128];
          //DWORD ReceivedBytes;
          DWORD BytesWritten;
          DATA Adat[256];
          short ActAdat=0;
          short nLed;
          int j;
          USHORT ErrorCnt;
          char Cadena[200];

          for(unsigned char i=0;i<128;++i)
          {
          	Adat\[i\].Address = 0;
          	Adat\[i\].Data = 0;
          	Adat\[128+i\].Address = 0;
          	Adat\[128+i\].Data = 0;
          }
          
          
          int k,l;
          k=SafeArrayGetLBound(\*CBLeds,1,&LBound);
          l=SafeArrayGetUBound(\*CBLeds,1,&UBound);
          sprintf(Cadena,"Valores %x %x %d %d",k,l,LBound,UBound);
          MessageBox(NULL,Cadena,"Titol",0);
          
              return 0;
          

          }

          Any Idea what is happen?

          _ Offline
          _ Offline
          _Erik_
          wrote on last edited by
          #4

          The fact that you do not get a runtime exception while running it makes me think that the function is correctly imported, and the IntPtr array along with all the other stuff regarding the GCHandle structure is fine as well. However, looking at the documentation for SafeArrayGetLBound function, I think we were missing something important. It says the array descriptor must have been created with SafeArrayCreate function. I am not used to manage this kind of pointers so I cannot be sure of this, but maybe we should also import SafeArrayCreate function and use it to create the array descriptor before passing it to the EncenderLEDs function. Does it make sense?

          E 1 Reply Last reply
          0
          • _ _Erik_

            The fact that you do not get a runtime exception while running it makes me think that the function is correctly imported, and the IntPtr array along with all the other stuff regarding the GCHandle structure is fine as well. However, looking at the documentation for SafeArrayGetLBound function, I think we were missing something important. It says the array descriptor must have been created with SafeArrayCreate function. I am not used to manage this kind of pointers so I cannot be sure of this, but maybe we should also import SafeArrayCreate function and use it to create the array descriptor before passing it to the EncenderLEDs function. Does it make sense?

            E Offline
            E Offline
            edmonson
            wrote on last edited by
            #5

            May be logic, but calling DLL from VB6 no safearray creation is needed. I will continue investigating. If I find a solution, I tell you. Regards.

            _ 1 Reply Last reply
            0
            • E edmonson

              May be logic, but calling DLL from VB6 no safearray creation is needed. I will continue investigating. If I find a solution, I tell you. Regards.

              _ Offline
              _ Offline
              _Erik_
              wrote on last edited by
              #6

              Yes, you are right, but remember arrays in VB6 were allocated as SAFEARRAY under the covers, and that is quite a different thing than .NET arrays, and it seems you are using VB.NET.

              E 1 Reply Last reply
              0
              • _ _Erik_

                Yes, you are right, but remember arrays in VB6 were allocated as SAFEARRAY under the covers, and that is quite a different thing than .NET arrays, and it seems you are using VB.NET.

                E Offline
                E Offline
                edmonson
                wrote on last edited by
                #7

                Sure. But the DLL is now compiled on VS2008. Reading about safearrays and thinking about your suggest, I've find an assembly named system.visualstudio.ole.interop on VS2008 help that implements Safearray struct. I'll try to use it, but from now, I can't load the assembly. It doesn't appear on Net list references, but it is present on Windows\assembly. If I achieve to load it, I tell you about. Regards.

                1 Reply Last reply
                0
                Reply
                • Reply as topic
                Log in to reply
                • Oldest to Newest
                • Newest to Oldest
                • Most Votes


                • Login

                • Don't have an account? Register

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • World
                • Users
                • Groups