Page 1 of 2 12 LastLast
Results 1 to 40 of 62

Thread: C++ DLL to VB6

  1. #1

    Thread Starter
    New Member
    Join Date
    Feb 2015
    Posts
    1

    C++ DLL to VB6

    Hi.

    I have an relay card control dll and also header file for it for c++. I would like to use it in VB6. May you please help how to make a definitions module for the dll. Please find the header below:

    #ifndef USB_RELAY_DEVICE_H__
    #define USB_RELAY_DEVICE_H__

    #pragma comment(lib, "setupapi.lib")

    #ifdef __cplusplus
    extern "C" {
    #endif

    //#pragma comment(lib, "setupapi.lib")

    #ifdef _WIN32
    #define EXPORT_API __declspec(dllexport)
    #else
    #define EXPORT_API
    #endif

    enum usb_relay_device_type
    {
    USB_RELAY_DEVICE_ONE_CHANNEL = 1,
    USB_RELAY_DEVICE_TWO_CHANNEL = 2,
    USB_RELAY_DEVICE_FOUR_CHANNEL = 4,
    USB_RELAY_DEVICE_EIGHT_CHANNEL = 8
    };

    /*usb relay board info structure*/
    struct usb_relay_device_info
    {
    unsigned char *serial_number;
    char *device_path;
    usb_relay_device_type type;
    usb_relay_device_info* next;
    };

    /*init the USB Relay Libary
    @returns: This function returns 0 on success and -1 on error.
    */
    int EXPORT_API usb_relay_init(void);

    /*Finalize the USB Relay Libary.
    This function frees all of the static data associated with
    USB Relay Libary. It should be called at the end of execution to avoid
    memory leaks.
    @returns:This function returns 0 on success and -1 on error.
    */
    int EXPORT_API usb_relay_exit(void);


    /*Enumerate the USB Relay Devices.*/
    struct usb_relay_device_info EXPORT_API * usb_relay_device_enumerate(void);


    /*Free an enumeration Linked List*/
    void EXPORT_API usb_relay_device_free_enumerate(struct usb_relay_device_info*);

    /*open device that serial number is serial_number*/
    /*@return: This funcation returns a valid handle to the device on success or NULL on failure.*/
    /*e.g: usb_relay_device_open_with_serial_number("abcde", 5")*/
    int EXPORT_API usb_relay_device_open_with_serial_number(const char *serial_number, unsigned len);

    /*open a usb relay device
    @return: This funcation returns a valid handle to the device on success or NULL on failure.
    */
    int EXPORT_API usb_relay_device_open(struct usb_relay_device_info* device_info);

    /*close a usb relay device*/
    void EXPORT_API usb_relay_device_close(int hHandle);

    /*open a relay channel on the USB-Relay-Device
    @paramter: index -- which channel your want to open
    hHandle -- which usb relay device your want to operate
    @returns: 0 -- success; 1 -- error; 2 -- index is outnumber the number of the usb relay device
    */
    int EXPORT_API usb_relay_device_open_one_relay_channel(int hHandle, int index);

    /*open all relay channel on the USB-Relay-Device
    @paramter: hHandle -- which usb relay device your want to operate
    @returns: 0 -- success; 1 -- error
    */
    int EXPORT_API usb_relay_device_open_all_relay_channel(int hHandle);

    /*close a relay channel on the USB-Relay-Device
    @paramter: index -- which channel your want to close
    hHandle -- which usb relay device your want to operate
    @returns: 0 -- success; 1 -- error; 2 -- index is outnumber the number of the usb relay device
    */
    int EXPORT_API usb_relay_device_close_one_relay_channel(int hHandle, int index);

    /*close all relay channel on the USB-Relay-Device
    @paramter: hHandle -- which usb relay device your want to operate
    @returns: 0 -- success; 1 -- error
    */
    int EXPORT_API usb_relay_device_close_all_relay_channel(int hHandle);

    /*
    status bit: High --> Low 0000 0000 0000 0000 0000 0000 0000 0000, one bit indicate a relay status.
    the lowest bit 0 indicate relay one status, 1 -- means open status, 0 -- means closed status.
    bit 0/1/2/3/4/5/6/7/8 indicate relay 1/2/3/4/5/6/7/8 status
    @returns: 0 -- success; 1 -- error
    */
    int EXPORT_API usb_relay_device_get_status(int hHandle, unsigned int *status);

    #ifndef EXPORT_DLL
    int EXPORT_API usb_relay_device_set_serial(int hHandle, char serial[5]);
    #endif

    #ifdef __cplusplus
    }
    #endif

    #endif //end of ifdef __cplusplus

  2. #2
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: C++ DLL to VB6

    You have to declare the functions exported by the DLL in your VB6 application. For example:-
    c Code:
    1. int EXPORT_API usb_relay_device_get_status(int hHandle, unsigned int *status);

    The above should look like this in VB6:-
    vb Code:
    1. Declare Function usb_relay_device_get_status Lib "libname.dll" (ByVal hHandle as Long, ByRef status As Long) As Long
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  3. #3
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: C++ DLL to VB6

    Try this:

    Code:
    Option Explicit 'In a standard (.BAS) module
    
    Public Enum USB_Relay_Device_Type
        USB_RELAY_DEVICE_ONE_CHANNEL = 1
        USB_RELAY_DEVICE_TWO_CHANNEL = 2
        USB_RELAY_DEVICE_FOUR_CHANNEL = 4
        USB_RELAY_DEVICE_EIGHT_CHANNEL = 8
    End Enum
    
    Public Type USB_Relay_Device_Info
        Serial_Number As Long
        Device_Path   As Long
        Type          As USB_Relay_Device_Type
        Next          As Long
    End Type
    
    Public Declare Function usb_relay_device_close_all_relay_channel Lib "FileName_Of_Your_C++_DLL" (ByVal hHandle As Long) As Long
    Public Declare Function usb_relay_device_close_one_relay_channel Lib "FileName_Of_Your_C++_DLL" (ByVal hHandle As Long, ByVal Index As Long) As Long
    Public Declare Function usb_relay_device_enumerate Lib "FileName_Of_Your_C++_DLL" () As Long
    Public Declare Function usb_relay_device_get_status Lib "FileName_Of_Your_C++_DLL" (ByVal hHandle As Long, ByRef Status As Long) As Long
    Public Declare Function usb_relay_device_open Lib "FileName_Of_Your_C++_DLL" (ByRef Device_Info As USB_Relay_Device_Info) As Long
    Public Declare Function usb_relay_device_open_all_relay_channel Lib "FileName_Of_Your_C++_DLL" (ByVal hHandle As Long) As Long
    Public Declare Function usb_relay_device_open_one_relay_channel Lib "FileName_Of_Your_C++_DLL" (ByVal hHandle As Long, ByVal Index As Long) As Long
    Public Declare Function usb_relay_device_open_with_serial_number Lib "FileName_Of_Your_C++_DLL" (ByVal Serial_Number As String, ByVal Length As Long) As Long
    Public Declare Function usb_relay_device_set_serial Lib "FileName_Of_Your_C++_DLL" (ByVal hHandle As Long, ByVal Serial As Any) As Long
    Public Declare Function usb_relay_exit Lib "FileName_Of_Your_C++_DLL" () As Long
    Public Declare Function usb_relay_init Lib "FileName_Of_Your_C++_DLL" () As Long
    Public Declare Sub usb_relay_device_close Lib "FileName_Of_Your_C++_DLL" (ByVal hHandle As Long)
    Public Declare Sub usb_relay_device_free_enumerate Lib "FileName_Of_Your_C++_DLL" (ByRef Device_Info As USB_Relay_Device_Info)
    BTW, the VB6 manual has a chapter that teaches VB6 programmers how to work with standard DLLs written in other languages (such as C/C++). See Accessing DLLs and the Windows API.
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

  4. #4
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    This thread is almost 6 years old.. but I have the same issue/question. The original thread starter apparently never replied, so I don't know if e was successful with the answer.
    I want to drive these cheap AliExpres USB relay cards, such as https://www.aliexpress.com/item/4000...archweb201603_
    I found C++ sources including the usb_relay_device.dll
    Now I'm struggling in using this dll with VB6.
    I've tried as suggested in previous reply:
    Code:
    Public Declare Function usb_relay_device_open_with_serial_number Lib "usb_relay_device.dll" _
    (ByVal Serial_Number As String, ByVal Length As Long) As Long
    And call the function as:
    Code:
      MsgBox usb_relay_device_open_with_serial_number("BITFT", 5)
    But this results in the error message "Bad DLL calling convention"

    Anybody any idea if I can use this dll with VB6??
    _Wim_

  5. #5
    PowerPoster
    Join Date
    Nov 2017
    Posts
    3,116

    Re: C++ DLL to VB6

    It sounds like you've got the c++ code, can you post the function header from the c++ code? Also, you might try changing the 5 to 5& to use a long value explicitly.

  6. #6
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    The header file was already included in original message. Bottom-line the important stuff is:
    Code:
    #define EXPORT_API __declspec(dllexport)
    int EXPORT_API usb_relay_device_open_with_serial_number(const char *serial_number, unsigned len);
    adding the "&" declaring the variable explicitly as long, doesn't help
    Code:
      MsgBox usb_relay_device_open_with_serial_number("BITFT", 5&)
    _Wim_

  7. #7
    PowerPoster
    Join Date
    Nov 2017
    Posts
    3,116

    Re: C++ DLL to VB6

    Sorry, I didn't notice that the original post contained the header. I blame the lack of code formatting in the post and not my own laziness.


    You should find your answer here: https://www.vbforums.com/showthread....C-dll-s-in-VB6

    Good luck.

  8. #8
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    Hmmm, if I read that thread correctly, it says:
    VB only supports stdcall, but the default calling convention in c/c++ is cdecl
    ... and it seems that this dll uses cdecl..

    So it can't be done??
    _Wim_

  9. #9
    PowerPoster
    Join Date
    Nov 2017
    Posts
    3,116

    Re: C++ DLL to VB6

    Quote Originally Posted by _Wim_ View Post
    Hmmm, if I read that thread correctly, it says:

    ... and it seems that this dll uses cdecl..

    So it can't be done??
    _Wim_
    No idea, but it seemed like that other thread encompassed your scenario fairly closely, and had much more discussion than this one. Someone else with more experience with calling functions from C++ dll's will need to chime in to assist further.

    Good luck.

  10. #10
    King of sapila
    Join Date
    Oct 2006
    Location
    Greece
    Posts
    6,597

    Re: C++ DLL to VB6

    I don't know vb6 but the VB.NET equivalent is this:

    Code:
    <DllImport("usb_relay_device.dll", EntryPoint:="usb_relay_device_open_with_serial_number", SetLastError:=True, CharSet:=CharSet.Ansi, ExactSpelling:=True, CallingConvention:=CallingConvention.Cdecl)>
    Public Shared Function usb_relay_device_open_with_serial_number(
    <MarshalAs(UnmanagedType.LPStr)> ByVal serial_number As String, ByVal len As Integer) As Integer
    ἄνδρα μοι ἔννεπε, μοῦσα, πολύτροπον, ὃς μάλα πολλὰ
    πλάγχθη, ἐπεὶ Τροίης ἱερὸν πτολίεθρον ἔπερσεν·

  11. #11
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    As far as I know, there is no 'DLLImport' function in VB6.. But correct me if I'm wrong.
    I have some source code for the dll (friom GitHub), that mentions:
    LIBRARY USB_RELAY_DEVICE
    VERSION 1.0

    EXPORTS
    ; All exports are CDECL
    The thread pointed to me, showed actually:
    VB only supports stdcall, but the default calling convention in c/c++ is cdecl
    (actually VB also supports cdecl, but for that you must compile your project to native code).
    ...is that really an option to use dll's that are using CDECL ?? That is "compile to native code"? How? Project properties are standard set to "Compile to native code".. Any expert out there who can help me?
    _Wim_

  12. #12
    PowerPoster
    Join Date
    Nov 2017
    Posts
    3,116

    Re: C++ DLL to VB6

    The thread pointed to you also notes how you should be passing a String to a C++ function that expects a String pointer. Did you make that change to your VB6 code?

    The other note basically means that if you are encountering this error when you are running your VB6 program from within the VB6 IDE, then you should compile your program and try running the exe file and see if the issue persists, because it may well function properly at that point.

  13. #13
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: C++ DLL to VB6

    Quote Originally Posted by _Wim_ View Post
    ...is that really an option to use dll's that are using CDECL ?? That is "compile to native code"? How? Project properties are standard set to "Compile to native code".. Any expert out there who can help me?
    _Wim_
    I recommend you PM either Schmidt or The Trick. If someone knows how to hack VB6 to do this, it would be one of them.

    Quote Originally Posted by OptionBase1 View Post
    The thread pointed to you also notes how you should be passing a String to a C++ function that expects a String pointer. Did you make that change to your VB6 code?

    The other note basically means that if you are encountering this error when you are running your VB6 program from within the VB6 IDE, then you should compile your program and try running the exe file and see if the issue persists, because it may well function properly at that point.
    I'm a little rusty on this stuff but I'm not entirely sure that using StrPtr to pass the String is the right way. The reason is because that C function looks like expects a C style String. VB6 uses a COM type String referred to as a BSTR. I don't know if StrPtr would convert that String. I don't expect it would. If my memory serves me right, I think you're supposed to pass the String from VB6 by value as you normally would to any VB6 function except in the case of a function that was imported from a DLL, VB6 would automatically marshal it correctly as a pointer to an ANSI String. For Unicode Strings, you have to do a little magic with StrConv but my memory is very fuzzy on the details. One of the VB6 regulars should probably chime in on this. I used to know this stuff quite well but I haven't use VB6 seriously in like 6 years.
    Last edited by Niya; Jan 10th, 2021 at 01:28 AM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  14. #14
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: C++ DLL to VB6

    Quote Originally Posted by _Wim_ View Post
    ...is that really an option to use dll's that are using CDECL ?...
    You can call cdecl-defined functions also without compiling to a native-binary first,
    when you use the DispCallFunc-API (no "hacking" needed) - e.g. via the module below:
    Code:
    Option Explicit
    
    Private Declare Function DispCallFunc Lib "oleaut32" (ByVal pvInstance As Long, ByVal offsetinVft As Long, ByVal CallConv As Long, ByVal retTYP As Integer, ByVal paCNT As Long, ByRef paTypes As Integer, ByRef paValues As Long, ByRef retVAR As Variant) As Long
    Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
    Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
    Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
    Private Declare Function lstrlenA Lib "kernel32" (ByVal lpString As Long) As Long
    Private Declare Function lstrlenW Lib "kernel32" (ByVal lpString As Long) As Long
    Private Declare Sub RtlMoveMemory Lib "kernel32" (Dst As Any, Src As Any, ByVal BLen As Long)
    
    Private Enum CALLINGCONVENTION_ENUM
      CC_FASTCALL
      CC_CDECL
      CC_PASCAL
      CC_MACPASCAL
      CC_STDCALL
      CC_FPFASTCALL
      CC_SYSCALL
      CC_MPWCDECL
      CC_MPWPASCAL
    End Enum
    
    Private LibHdls As New Collection, VType(0 To 63) As Integer, VPtr(0 To 63) As Long
    
    Public Function stdCallW(sDll As String, sFunc As String, ByVal RetType As VbVarType, ParamArray P() As Variant)
    Dim i As Long, V(), HRes As Long
     
      V = P 'make a copy of the params, to prevent problems with VT_Byref-Members in the ParamArray
      For i = 0 To UBound(V)
        If VarType(P(i)) = vbString Then V(i) = StrPtr(P(i))
        VType(i) = VarType(V(i))
        VPtr(i) = VarPtr(V(i))
      Next i
      
      HRes = DispCallFunc(0, GetFuncPtr(sDll, sFunc), CC_STDCALL, RetType, i, VType(0), VPtr(0), stdCallW)
      If HRes Then Err.Raise HRes
    End Function
    
    Public Function cdeclCallW(sDll As String, sFunc As String, ByVal RetType As VbVarType, ParamArray P() As Variant)
    Dim i As Long, pFunc As Long, V(), HRes As Long
     
      V = P 'make a copy of the params, to prevent problems with VT_Byref-Members in the ParamArray
      For i = 0 To UBound(V)
        If VarType(P(i)) = vbString Then V(i) = StrPtr(P(i))
        VType(i) = VarType(V(i))
        VPtr(i) = VarPtr(V(i))
      Next i
      
      HRes = DispCallFunc(0, GetFuncPtr(sDll, sFunc), CC_CDECL, RetType, i, VType(0), VPtr(0), cdeclCallW)
      If HRes Then Err.Raise HRes
    End Function
    
    Public Function stdCallA(sDll As String, sFunc As String, ByVal RetType As VbVarType, ParamArray P() As Variant)
    Dim i As Long, pFunc As Long, V(), HRes As Long
     
      V = P 'make a copy of the params, to prevent problems with VT_Byref-Members in the ParamArray
      For i = 0 To UBound(V)
        If VarType(P(i)) = vbString Then P(i) = StrConv(P(i), vbFromUnicode): V(i) = StrPtr(P(i))
        VType(i) = VarType(V(i))
        VPtr(i) = VarPtr(V(i))
      Next i
      
      HRes = DispCallFunc(0, GetFuncPtr(sDll, sFunc), CC_STDCALL, RetType, i, VType(0), VPtr(0), stdCallA)
      
      For i = 0 To UBound(P) 'back-conversion of the ANSI-String-Results
        If VarType(P(i)) = vbString Then P(i) = StrConv(P(i), vbUnicode)
      Next i
      If HRes Then Err.Raise HRes
    End Function
    
    Public Function cdeclCallA(sDll As String, sFunc As String, ByVal RetType As VbVarType, ParamArray P() As Variant)
    Dim i As Long, pFunc As Long, V(), HRes As Long
     
      V = P 'make a copy of the params, to prevent problems with VT_Byref-Members in the ParamArray
      For i = 0 To UBound(V)
        If VarType(P(i)) = vbString Then P(i) = StrConv(P(i), vbFromUnicode): V(i) = StrPtr(P(i))
        VType(i) = VarType(V(i))
        VPtr(i) = VarPtr(V(i))
      Next i
      
      HRes = DispCallFunc(0, GetFuncPtr(sDll, sFunc), CC_CDECL, RetType, i, VType(0), VPtr(0), cdeclCallA)
      
      For i = 0 To UBound(P) 'back-conversion of the ANSI-String-Results
        If VarType(P(i)) = vbString Then P(i) = StrConv(P(i), vbUnicode)
      Next i
      If HRes Then Err.Raise HRes
    End Function
    
    Public Function vtblCall(pUnk As Long, ByVal vtblIdx As Long, ParamArray P() As Variant)
    Dim i As Long, V(), HRes As Long
      If pUnk = 0 Then Exit Function
    
      V = P 'make a copy of the params, to prevent problems with VT_ByRef-Members in the ParamArray
      For i = 0 To UBound(V)
        VType(i) = VarType(V(i))
        VPtr(i) = VarPtr(V(i))
      Next i
      
      HRes = DispCallFunc(pUnk, vtblIdx * 4, CC_STDCALL, vbLong, i, VType(0), VPtr(0), vtblCall)
      If HRes Then Err.Raise HRes
    End Function
    
    Public Function GetFuncPtr(sDll As String, sFunc As String) As Long
    Static hLib As Long, sLib As String
      If sLib <> sDll Then 'just a bit of caching, to make resolving libHdls faster
        sLib = sDll
        On Error Resume Next
          hLib = 0
          hLib = LibHdls(sLib)
        On Error GoTo 0
        
        If hLib = 0 Then
          hLib = LoadLibrary(sLib)
          If hLib = 0 Then Err.Raise vbObjectError, , "Dll not found (or loadable): " & sLib
          LibHdls.Add hLib, sLib '<- cache it under the dll-name for the next call
        End If
      End If
      GetFuncPtr = GetProcAddress(hLib, sFunc)
      If GetFuncPtr = 0 Then Err.Raise 453, , "EntryPoint not found: " & sFunc & " in: " & sLib
    End Function
    
    Public Function GetBStrFromPtr(lpSrc As Long, Optional ByVal ANSI As Boolean) As String
    Dim SLen As Long
      If lpSrc = 0 Then Exit Function
      If ANSI Then SLen = lstrlenA(lpSrc) Else SLen = lstrlenW(lpSrc)
      If SLen Then GetBStrFromPtr = Space$(SLen) Else Exit Function
          
      Select Case ANSI
        Case True: RtlMoveMemory ByVal GetBStrFromPtr, ByVal lpSrc, SLen
        Case Else: RtlMoveMemory ByVal StrPtr(GetBStrFromPtr), ByVal lpSrc, SLen * 2
      End Select
    End Function
    
    Public Sub CleanupLibHandles() 'not really needed - but callable (usually at process-shutdown) to clear things up
    Dim LibHdl
      For Each LibHdl In LibHdls: FreeLibrary LibHdl: Next
      Set LibHdls = Nothing
    End Sub
    Using the drop-in-module above, your code could then look like:

    Code:
    Function OpenDeviceWithSerial(SerialNumber As String) As Long
       OpenDeviceWithSerial = cdeclCallA(YourDllFilePath, "usb_relay_device_open_with_serial_number", _
                              vbLong, SerialNumber, Len(SerialNumber))
    End Function
    HTH

    Olaf
    Last edited by Schmidt; Jan 10th, 2021 at 03:31 AM.

  15. #15

  16. #16
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Thumbs up Re: C++ DLL to VB6

    Fantastic! That seems to work. I copied all your "drop-in-code" into a cdecl.bas module (without even trying to understand what it does..), and used:
    Code:
    Public Function OpenDeviceWithSerial(SerialNumber As String) As Long
       OpenDeviceWithSerial = cdeclCallA("usb_relay_device.dll", "usb_relay_device_open_with_serial_number", _
                              vbLong, SerialNumber, Len(SerialNumber))
    End Function
    (Note: I have the dll copied into SysWOW64 directory to avoid absolute paths)

    Then I tried:
    Code:
      MsgBox OpenDeviceWithSerial("BITFT")
    (These USB relays apparently all have a 5 digit code. Mine happens to be BITFT..)

    And it returns some high number, the hWnd handle I assume. Without the USB relay it returns 0, so it appears to work this way. Next will be the other functions, like enumerate, etc.. But I assume that should all be possible!! Thanks very much, Olaf

  17. #17
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: C++ DLL to VB6

    @Olaf: Do VB6 Declares call FreeLibrary on tear-down?

    IMO the LibHdls collection can be safely replaced by a simple hLib = GetModuleHandle(sLib) and skip the CleanupLibHandles sub altogether.

    This will allow OP to manually LoadLibrary the DLL in Sub Main/Form_Load (from App.Path or App.Path & "\External") and continue calling cdeclCallA with simple "usb_relay_device.dll" with no path specified without requiring a copy of the DLL somewhere in PATH.

    This approach to DLL discovery works fine with VB6 Declares so might be useful to support it here too.

    cheers,
    </wqw>

  18. #18
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: C++ DLL to VB6

    I have to admit, I didn't know it could be done like this. My first instinct was to actually do the CDECL call manually through assembly. I was thinking of setting up a piece of assembly code(self modifying code) in memory that can be called using STDCALL and the assembly code would call the function using the CDECL calling convention. I've experimented with something similar in .Net and it's actually not as difficult as I imagined it would be.

    Though come to think of it, as I'm sitting here right now I can't think of a way to call the assembly code from VB6. .Net's Marshal class can wrap an unmanaged function pointer in a delegate so it can be called like any other function. I can't remember if VB6 has a way to do that. Oh well. Interesting problem to brainstorm.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  19. #19
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: C++ DLL to VB6

    Quote Originally Posted by Niya View Post
    I have to admit, I didn't know it could be done like this. My first instinct was to actually do the CDECL call manually through assembly. I was thinking of setting up a piece of assembly code(self modifying code) in memory that can be called using STDCALL and the assembly code would call the function using the CDECL calling convention. I've experimented with something similar in .Net and it's actually not as difficult as I imagined it would be.
    The Trick's link above points to a thread here where several such trampolines were discussed recently, incl. self-modifying ones.

    The trampolines seem to be the fastest approach to bridging stdcall and cdecl calling conventions and what I would personally implement but many people prefer to not trigger A/Vs with "virus-like" techniques so use DispCallFunc.

    cheers,
    </wqw>

  20. #20
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: C++ DLL to VB6

    Quote Originally Posted by _Wim_ View Post
    Fantastic! That seems to work. I copied all your "drop-in-code" into a cdecl.bas module (without even trying to understand what it does..), and used:
    Code:
    Public Function OpenDeviceWithSerial(SerialNumber As String) As Long
       OpenDeviceWithSerial = cdeclCallA("usb_relay_device.dll", "usb_relay_device_open_with_serial_number", _
                              vbLong, SerialNumber, Len(SerialNumber))
    End Function
    (Note: I have the dll copied into SysWOW64 directory to avoid absolute paths)
    You could also place your Device-Dll in a "Bin-Path" - relative to the Executable...
    (that's what I usually prefer, to make Apps XCopy-deployable from a Zip).
    Your Dll-Path-specifier could then be e.g.: cdeclCallA(App.Path & "\Bin\usb_relay_device.dll", ...

    Quote Originally Posted by _Wim_ View Post
    Then I tried:
    Code:
      MsgBox OpenDeviceWithSerial("BITFT")
    (These USB relays apparently all have a 5 digit code. Mine happens to be BITFT..)

    And it returns some high number, the hWnd handle I assume.
    It's a Device-Handle (a so called "opaque type"... similar to a hWnd - but not "a hWnd").

    Quote Originally Posted by _Wim_ View Post
    Without the USB relay it returns 0, so it appears to work this way.
    Next will be the other functions, like enumerate, etc.. But I assume that should all be possible!!
    I'd suggest to encapsulate this in a nice little Class...
    (holding the Device-handle internally, so that you can avoid it in the Parameter-Defs of your Class-Methods).

    Another advantage of a Class is, that it will "auto-cleanup" the internal Handle (closing the Device), when the Class-Instance is terminated.

    E.g. (as a starting-point):
    Code:
    Option Explicit
    
    Const retVoid As Long = 0
    Private mDll As String, mHdl As Long
    
    Private Sub Class_Initialize()
      mDll = App.Path & "\Bin\usb_relay_device.dll" 'specify the Dll-Path
    End Sub
    
    Public Function OpenDeviceWithSerial(SerialNumber As String) As Boolean
      CloseDevice
      mHdl = cdeclCallA(mDll, "usb_relay_device_open_with_serial_number", _
                              vbLong, SerialNumber, Len(SerialNumber))
      If mHdl Then OpenDeviceWithSerial = True 'now only returning a Boolean (the Handle is kept internally)
    End Function
    
    '... your other Public Methods ...
    '    which don't have to define the Handle as a Parameter, since it is kept Class-internally
    
    Public Sub CloseDevice()
      If mHdl = 0 Then Exit Sub
      cdeclCallA mDll, "usb_relay_device_close", retVoid, mHdl 'note the void-return type
      mHdl = 0
    End Sub
     
    Private Sub Class_Terminate()
      CloseDevice 'auto-close when the class-instance goes out of scope
    End Sub
    As for performance (since others brought it up)...

    It is true that the DispCallFunc-approach has a higher overhead, e.g. compared to using (cdecl-marked) TypeLib-defs,
    but this is only relevant in scenarios, where you have to perform thousands of Dll-calls in a high frequency (in some inner loop).

    To switch a few relais on and off occasionally, that's a scenario where the overhead is not really noticable.

    @wqweto
    AFAIK FreeLibrary is not really necessary, since all these "Module-handles" are cleaned up anyways, when a Process terminates.
    IMO the API is needed only in "Plugin-like scenarios" (e.g. when you want to overwrite a Plugin-Dll in your App-specific Plugin-Directory, whilst the App-Process is still running).

    Olaf
    Last edited by Schmidt; Jan 10th, 2021 at 07:25 AM.

  21. #21

  22. #22
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: C++ DLL to VB6

    Quote Originally Posted by wqweto View Post
    The Trick's link above points to a thread here where several such trampolines were discussed recently, incl. self-modifying ones.

    The trampolines seem to be the fastest approach to bridging stdcall and cdecl calling conventions and what I would personally implement but many people prefer to not trigger A/Vs with "virus-like" techniques so use DispCallFunc.

    cheers,
    </wqw>
    Wow, your Call_ultow and pvPatchTrampoline are very clever. It was very creative how you used the pointer to one of your own module's functions as a means of executing self-modified code. Not gonna lie, it's one of the most clever work-arounds I've seen in a while. I gotta come back and rep you for that piece of gymnastics.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  23. #23
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: C++ DLL to VB6

    Quote Originally Posted by Niya View Post
    Wow, your Call_ultow and pvPatchTrampoline are very clever. It was very creative how you used the pointer to one of your own module's functions as a means of executing self-modified code. Not gonna lie, it's one of the most clever work-arounds I've seen in a while. I gotta come back and rep you for that piece of gymnastics.
    All kudos to The Trick -- I just implemented the idea w/ the empty param.

    cheers,
    </wqw>

  24. #24
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    ...ok, next 'challenge' ... One of the functions return a struct.
    So I have this code in a module (.bas):
    Code:
    Public Enum USB_Relay_Device_Type
        USB_RELAY_DEVICE_ONE_CHANNEL = 1
        USB_RELAY_DEVICE_TWO_CHANNEL = 2
        USB_RELAY_DEVICE_FOUR_CHANNEL = 4
        USB_RELAY_DEVICE_EIGHT_CHANNEL = 8
    End Enum
    
    Public Type USB_Relay_Device_Info
        Serial_Number As Long
        Device_Path   As Long
        Type          As USB_Relay_Device_Type
        Next          As Long
    End Type  
    
    Public Function EnumerateDevice() As USB_Relay_Device_Info
      Call cdeclCallA("usb_relay_device.dll", "usb_relay_device_enumerate", vbUserDefinedType, 0)
    End Function
    But that doesn't work when I use:
    Code:
    Option Explicit
    
    Dim DevInfo As USB_Relay_Device_Info
    
    Private Sub Form_Load()
      DevInfo = EnumerateDevice()
    End Sub
    So return type is a userdefined type, and there are no parameters, that's why I used 0 for the parameters..
    What's wrong here??? Your help is very much appreciated...
    _Wim_

  25. #25
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: C++ DLL to VB6

    Please post the C/C++ function declaration like this

    pusb_relay_device_info_t USBRL_API usb_relay_device_enumerate(void)

    So the retval is *pointer* to this USB_Relay_Device_Info UDT. You can treat pointers as glorified Longs like this

    Dim lPtr As Long
    lPtr = cdeclCallA("usb_relay_device.dll", "usb_relay_device_enumerate", vbLong, 0)

    And then use CopyMemory to dereference it

    Dim uRetVal As USB_Relay_Device_Info
    Call CopyMemory(uRetVal, ByVal lPtr, Len(uRetVal))

    cheers,
    </wqw>

  26. #26
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    Hmm this is getting a bit complicated for me to understand. First the 'cdeclCallA' function doesn't return anything, as far as I see. [EDIT: I read from some helpfiles that "DispCallFunc" returns the function result. I'll try your suggestion]
    So lPtr = cdeclCallA(.. won't do anything!?
    As a matter of fact I'm also trying the GetStatus routine, that *returns* some value. So I used 'ByRef', but that crashes VB6 IDE...

    Code:
    Public Function GetStatus(ByVal hHandle As Long, ByRef Status As Long) As Long
      Call cdeclCallA("usb_relay_device.dll", "usb_relay_device_get_status", vbLong, hHandle, Status)
    End Function
    ..could use some (more) help here ... :-)
    _Wim_
    Last edited by _Wim_; Jan 10th, 2021 at 12:29 PM.

  27. #27
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: C++ DLL to VB6

    There is an extra 0 in cdeclCallA call in my reply above that has to be removed.

    If usb_relay_device_enumerate returns NULL then it probably means some kind of erroe occurred.

  28. #28
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    OK, what about this:
    Code:
    Public Function EnumerateDevice() As USB_Relay_Device_Info
    Dim lPtr As Long, uRetVal As USB_Relay_Device_Info
      lPtr = cdeclCallA("usb_relay_device.dll", "usb_relay_device_enumerate", vbLong)
      Call CopyMemory(uRetVal, ByVal lPtr, Len(uRetVal))
      EnumerateDevice = uRetVal
    End Function
    And I call it with:
    Code:
    Dim DevInfo As USB_Relay_Device_Info
      DevInfo = EnumerateDevice
    This crashes IDE. Single stepping reveiled that lPtr=0 after calling cdeclCallA. So the CopyMemory crashes...
    Now what?
    _Wim_

  29. #29
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: C++ DLL to VB6

    That's what I meant by if usb_relay_device_enumerate returns NULL then it probably means some kind of error occurred within the DLL.

    NULL retval usually indicates "unable to retrieve info" for some reason.

    Best practice is your wrapper to check for NULL and not crash but indicate error by returning false or raising an error on its own.

    cheers,
    </wqw>

  30. #30

  31. #31
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    Thanks, indeed the function returned 0, because I first has opened the device. Apparently you can't enumerate if device is opened... Makes sense. Indeed the wrapper should check for a non-zero value. I'll add. At least I have some values returned by the function, now I'll have to find out how to handle the device info struct...

    Meanwhile any comments on why the "usb_relay_device_get_status" crashes? This DLL routine returns a long value back into the parameter 'Status', not as function result, as far as I can see. So I thought using ByRef instead of ByVal. But that causes to crashes VB6 IDE..

    Code:
    Public Function GetStatus(ByVal hHandle As Long, ByRef Status As Long) As Long
      Call cdeclCallA("usb_relay_device.dll", "usb_relay_device_get_status", vbLong, hHandle, Status)
    End Function
    ... to be continued

  32. #32

  33. #33
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    Thank you, thank you, thank you... The varptr(status) did it. I can now enumerate, open device, get status, close ... Next is actually open and close the relay, but I'm sure it will be similar/same as all previous functions. Again many thanks to you experts!
    So now I can work on the actual application...
    _Wim_

  34. #34
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: C++ DLL to VB6

    Quote Originally Posted by wqweto View Post
    Please post the C/C++ function declaration like this

    pusb_relay_device_info_t USBRL_API usb_relay_device_enumerate(void)

    So the retval is *pointer* to this USB_Relay_Device_Info UDT. You can treat pointers as glorified Longs like this

    Dim lPtr As Long
    lPtr = cdeclCallA("usb_relay_device.dll", "usb_relay_device_enumerate", vbLong, 0)

    And then use CopyMemory to dereference it

    Dim uRetVal As USB_Relay_Device_Info
    Call CopyMemory(uRetVal, ByVal lPtr, Len(uRetVal))

    cheers,
    </wqw>
    Minor point here. Isn't it a better practice to use LenB instead of just Len when requesting the size of a Structure?
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  35. #35
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: C++ DLL to VB6

    Quote Originally Posted by Niya View Post
    Minor point here. Isn't it a better practice to use LenB instead of just Len when requesting the size of a Structure?
    True, my mistake -- Len on UDTs is evil!

    And for vNext we might need LenB(USB_Relay_Device_Info) to work like sizeof in other languages at compile-time too :-))

    I often find myself declaring additional Private Const sizeof_USB_Relay_Device_Info As Long = 4 * 4 manually just to be sure the sizes match.

    cheers,
    </wqw>

  36. #36
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    One other question:
    What is the difference between cdeclCallA and cdeclCallW calls? It seems that it treats string type parameters differently!? Better question would be: when do I use one and when the other?
    On the same subject, why are there fucntions for stdCallA (and stdCallW)? If a DLL uses stdcall convention, then there is no need for wrapping calls to the DLL??
    _Wim_

  37. #37
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: C++ DLL to VB6

    Quote Originally Posted by _Wim_ View Post
    One other question:
    What is the difference between cdeclCallA and cdeclCallW calls? It seems that it treats string type parameters differently!? Better question would be: when do I use one and when the other?
    On the same subject, why are there fucntions for stdCallA (and stdCallW)? If a DLL uses stdcall convention, then there is no need for wrapping calls to the DLL??
    _Wim_
    I'm guessing he is following the Windows naming convention when it comes to functions that have String parameters. "W" means wide String which on modern Windows means a UTF-16 String. "A" means ANSI String which is the internal String format of Windows versions that don't use the Windows NT Kernel(Windows 95/98, 3.1 etc). It is very important when calling functions that take String parameters to know what format Strings are expected because if there is a mismatch between your code and the function you're calling, bad things will happen. For example the String "dog" as a UTF-16 String will occupy 6 bytes of memory while in ANSI format will occupy 3 bytes. You can use your imagination to figure out what can go wrong if you have a mismatch there. Encoding is another aspect of different String formats to take into consideration.

    In the case of this function:-
    Code:
    int EXPORT_API usb_relay_device_open_with_serial_number(const char *serial_number, unsigned len);
    That function prototype indicates that most likely what is expected is an ANSI String so you're going to want to use the "A" version. If it was expecting a wide String, you may have seen something like LPWSTR instead of char*.

    Also another thing to be ware of is when such functions ask for a length. Sometimes a function will want a buffer size in terms of bytes and sometimes in terms of character length. Pay attention with such functions. In the case of the above function, they are synonymous since ANSI Strings are 1 byte per character so a byte length with be same as a character length. A UTF-16 String of character length 10 will have a byte length of 20 since in this case it's 2 bytes per character.

    If the functions involved have no String parameters then it doesn't matter at all which version you use. They will effectively be identical.
    Last edited by Niya; Jan 11th, 2021 at 04:18 AM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  38. #38
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: C++ DLL to VB6

    Quote Originally Posted by _Wim_ View Post
    On the same subject, why are there fucntions for stdCallA (and stdCallW)? If a DLL uses stdcall convention, then there is no need for wrapping calls to the DLL??
    The module is basically the same one, which works under the cover of the vbRichClient-COM-lib
    (where these call-supporting Methods sit behind a COM-Class - and are accessible via New_c.stdCall(...)).

    And yes, if you work in VB6 or VBA, then you will not need the stdcall-methods (because the Declare statement is supported) -
    but there's scripting-languages which don't support Declare - as e.g. JavaScript, VBScript or *.asp-Pages,
    where these stdcall-helper-methods will allow "external Dll-access" (via an appropriate COM-Class-instance, in case the language has COM-support).

    Olaf

  39. #39
    Lively Member
    Join Date
    Feb 2006
    Posts
    92

    Re: C++ DLL to VB6

    Quote Originally Posted by _Wim_ View Post
    ... now I can work on the actual application...
    _Wim_
    Can you post a sample code and usb_relay_device.dll file ?

  40. #40
    Lively Member
    Join Date
    Oct 2009
    Location
    Nijmegen, Netherlands
    Posts
    64

    Re: C++ DLL to VB6

    For the .dll refer to for instance http://www.giga.co.za/ocart/index.ph...product_id=229, or google on "usb-relay-2"

    FWIW, so far I have this "wrapper" code, but it is 'under-construction'!!
    Code:
    Option Explicit
    Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal length As Long)
    
    Public Enum USB_Relay_Device_Type
        USB_RELAY_DEVICE_ONE_CHANNEL = 1
        USB_RELAY_DEVICE_TWO_CHANNEL = 2
        USB_RELAY_DEVICE_FOUR_CHANNEL = 4
        USB_RELAY_DEVICE_EIGHT_CHANNEL = 8
    End Enum
    
    Public Type USB_Relay_Device_Info
        Serial_Number As Long ' pointer to 5 digits serial number
        Device_Path   As Long ' I assume this is not implemented, or should return "NOTHING:"..!?
        Type          As USB_Relay_Device_Type
        Next          As Long ' original type is USB_Relay_Device_Info, so recursive use ..!
    End Type
    
    Public Function OpenDeviceWithSerial(SerialNumber As String) As Long ' return handle to device
    'usage: hnd=OpenDeviceWithSerial("ABCDE")
      OpenDeviceWithSerial = cdeclCallA("usb_relay_device.dll", "usb_relay_device_open_with_serial_number", _
                vbLong, SerialNumber, Len(SerialNumber))
    End Function
    
    Public Sub CloseDevice(ByVal hHandle As Long)
    'Close entire relay board
      If hHandle <> 0 Then Call cdeclCallA("usb_relay_device.dll", "usb_relay_device_close", vbEmpty, hHandle)
    End Sub
    
    ' TODO add another wrapper on top of EnumerateDevice to collect all devices in an array, and serial nr translated into string(s)
    Public Function EnumerateDevice() As USB_Relay_Device_Info
    'Get device info, return as "USB_Relay_Device_Info"
    Dim lPtr As Long, uRetVal As USB_Relay_Device_Info
      lPtr = cdeclCallA("usb_relay_device.dll", "usb_relay_device_enumerate", vbLong)
      If lPtr <> 0 Then
        Call CopyMemory(uRetVal, ByVal lPtr, LenB(uRetVal))
        EnumerateDevice = uRetVal
      End If
    End Function
    
    Public Function GetStatus(ByVal hHandle As Long, ByRef Status As Long) As Long
    ' get open/close status of the relay switches. Although long is returned, only LSB byte is used
    ' '3' = two switches are open
      If hHandle <> 0 Then Call cdeclCallA("usb_relay_device.dll", "usb_relay_device_get_status", vbLong, hHandle, VarPtr(Status))
    End Function
    
    Public Function RelayInit() As Long
    'Supposed to start the application with RelayInit, but appears not really necessary...!?
      Call cdeclCallA("usb_relay_device.dll", "usb_relay_init", vbEmpty)
    End Function
    
    Public Function RelayExit() As Long
    'Call on form unload
      Call cdeclCallA("usb_relay_device.dll", "usb_relay_exit", vbEmpty)
    End Function
    
    Public Function RelayOpen(Device_Info As Long) As Long
    'Open device using Device_Info obtained via EnumerateDevice
      If Device_Info <> 0 Then RelayOpen = cdeclCallA("usb_relay_device.dll", "usb_relay_device_open", vbLong, Device_Info)
    End Function
    
    Public Function OpenAllChannels(ByVal hHandle As Long)
    'Activates all relay switches.. Funny name for closing the relay contacts.. :-)
      If hHandle <> 0 Then OpenAllChannels = cdeclCallA("usb_relay_device.dll", "usb_relay_device_open_all_relay_channel", vbLong, hHandle)
    End Function
    
    Public Function OpenOneChannel(ByVal hHandle As Long, ByVal Index As Long)
    'Activates one relay contact, index = 1, 2, etc
      If hHandle <> 0 Then OpenOneChannel = cdeclCallA("usb_relay_device.dll", "usb_relay_device_open_one_relay_channel", vbLong, hHandle, Index)
    End Function
    
    Public Function CloseOneChannel(ByVal hHandle As Long, ByVal Index As Long)
    'Deactivate one relay contact, index = 1, 2, etc
      If hHandle <> 0 Then CloseOneChannel = cdeclCallA("usb_relay_device.dll", "usb_relay_device_close_one_relay_channel", vbLong, hHandle, Index)
    End Function
    
    Public Function CloseAllChannels(ByVal hHandle As Long)
    'Deactivates all relay switches.. Funny name for opening the relay contacts.. :-)
      If hHandle <> 0 Then CloseAllChannels = cdeclCallA("usb_relay_device.dll", "usb_relay_device_close_all_relay_channel", vbLong, hHandle)
    End Function
    
    ' FreeEnumerate crashes VB6 IDE
    'Public Function FreeEnumerate(ByVal Device_Info As Long) As Long
    '    If Device_Info <> 0 Then FreeEnumerate = cdeclCallA("usb_relay_device.dll", "usb_relay_device_free_enumerate", vbLong, Device_Info)
    'End Function
    And here are some of my calls, but these are even more 'under-construction'...

    Code:
    Option Explicit
    
    Dim hnd As Long
    Dim DevInfo As USB_Relay_Device_Info
    '
    
    Private Sub Form_Load()
    Dim SerNr As String, Status As Long, bstr(4) As Byte
      Call RelayInit
      
    '  hnd = OpenDeviceWithSerial("BITFT")
    '  MsgBox hnd
    
      DevInfo = EnumerateDevice()
      With DevInfo
        If .Serial_Number <> 0 Then
          Call CopyMemory(bstr(0), ByVal .Serial_Number, 5)
          SerNr = StrConv(bstr, vbUnicode)
        End If
      End With
    
    '
      MsgBox SerNr & " - " & DevInfo.Type
      hnd = RelayOpen(VarPtr(DevInfo))
      MsgBox hnd
    
      Call GetStatus(hnd, Status)
      MsgBox Status
    
    
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
      CloseDevice (hnd)
      RelayExit
      CleanupLibHandles
    End Sub
    
    Private Sub Check1_Click(Index As Integer)
      With Check1(Index)
        If .Value Then
          Call OpenOneChannel(hnd, Index + 1)
        Else
          Call CloseOneChannel(hnd, Index + 1)
        End If
      End With
    End Sub
    
    Private Sub Command1_Click()
      OpenAllChannels (hnd)
    End Sub
    
    Private Sub Command2_Click()
      CloseAllChannels (hnd)
    End Sub

    ... as said under construction, but open for any remarks of course!
    _Wim_
    Last edited by _Wim_; Jan 11th, 2021 at 03:13 PM.

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width