3

I'm working on a VB6 graphic interface and I need to make an implicit linking to a DLL.

The motivation for this comes from my previous question. The DLL in question uses static TLS, __declspec(thread), and of course this fails horribly when the DLL is linked explicitly using LoadLibray.

I'd really like to avoid modifications to the DLL, so does anyone know how to trick a VB6 executable to link to a specific DLL implicitly?

Community
  • 1
  • 1
Beppe
  • 381
  • 2
  • 7
  • 15
  • I've edited the question and the tags. I hope you approve. I've done this to try to improve the chances of you getting a good answer! I don't know the details of how to do this, or even if it will work, but my instincts tell me that it should work. – David Heffernan Mar 04 '11 at 15:40
  • @David, Yeah I have noticed it and I say Thank you! Now it is more clear, I'll try to follow your model next times. – Beppe Mar 04 '11 at 15:51

3 Answers3

5

Create an IDL file for your DLL that describes your exported functions in a moduleclause.

Compile with the MIDL compiler and reference the resulting tlb file from your VB6 project (Project - References).
And remove all Declare Functions.

The tlb file is only used for compilation (in this case), you don't have to include it into the setup.

GSerg
  • 76,472
  • 17
  • 159
  • 346
  • It's a nice idea, but I think VB6 will still load the library dynamically. – Ben Voigt Mar 04 '11 at 16:23
  • @GSerg I'm not familiar with IDL files and MIDL compiler, so it will take me some time to do it (right now I'm learning what an IDL file is). When I'm done I'll give you a feedback. Thank you very much. – Beppe Mar 04 '11 at 16:39
  • @Beppe I learned IDL specifically to do this trick. It's quite easy when you focus on the areas you need. The IDL example in the end of the MSDN article is basically all IDL code you need. – GSerg Mar 04 '11 at 16:44
  • @GSerg I tried it, but as soon as I delete the `Declare Function`s, the compiler says that these functions are not defined. – Beppe Mar 07 '11 at 14:17
  • @Beppe Then you haven't refernced the correct tlb, or the functions now have different names, or you didn't make the tlb functions visible (IDL error). If you press F2, can you see your object library (top left dropdown) and browse it? – GSerg Mar 07 '11 at 14:41
  • @GSerg I added in the question the three IDLs files I tried. What is the correct one? Thank you. – Beppe Mar 07 '11 at 14:55
  • @Beppe VB cannot use `char filename_in[]` and such, use `char*` or `wchar*`. What was the data type in your `Declare Function`? I assume `ByVal As String`. It doesn't mean the string goes byval though, the pointer to the string does. – GSerg Mar 07 '11 at 15:37
  • @GSerg Hello, I still have problems with the parameter to pass at the function. How should I declare string in the IDL file? Because using `char`, `char`, and `wchar*`, in the object menu it says `filename_in As `. And what type should I use for these variables in the VB6 environment? Thank you. – Beppe Mar 08 '11 at 09:15
  • 1
    @Beppe `String` in VB. In IDL I used `LPSTR`, which is essentially `char*`. – GSerg Mar 08 '11 at 17:28
2

Here is a sample IDL that import functions from standard OS dlls

[
  uuid(YOURTYPE-LIBG-UIDH-ERE0-000000000000),
  version(1.0),
  helpstring ("My Type Library 1.0")
]
library MyTypeLib
{
    importlib("stdole2.tlb");

    typedef struct {
        long    Data1;
        short   Data2;
        short   Data3;
        BYTE    Data4[8];
    } VBGUID;

    typedef VBGUID CLSID;

    [dllname("OLEAUT32")]
    module OleAut32
    {
        [entry("SysAllocString")]
        BSTR SysAllocString([in] long lpStr);
        ...
    };

    [dllname("USER32")]
    module User32
    {
        [entry("RegisterClipboardFormatA")]
        UINT RegisterClipboardFormat([in] LPSTR lpszFormat);
        [entry("FillRect")]
        DWORD FillRect([in] DWORD hDC, [in] int lpRect, [in] DWORD hBrush);
        ...
    };

    [dllname("BOGUS")]
    module Strings
    {
        const LPSTR CLSID_DsQuery = "{8A23E65E-31C2-11D0-891C-00A024AB2DBB}";
        const LPSTR CLSID_DsFindObjects = "{83EE3FE1-57D9-11D0-B932-00A024AB2DBB}";
        ...
    }
}
wqw
  • 11,771
  • 1
  • 33
  • 41
  • Thanks for the reply! I just have problems with variables that have a type defined by me, like if I do: `int myFunct([in] myStruct data);` the compiler says: `syntax error: expecting a type specification near "myStruct"`. Do you know how to solve this problem? Thanks – Beppe Mar 07 '11 at 09:03
  • You have to include the struct in the IDL (sample updated) or you can declare it as `long myFunct([in] long data)` and call it in VB6 with `myFunct VarPtr(uData)`. – wqw Mar 07 '11 at 09:12
  • can u post an example of struct inclusion in the IDL file? Thanks – Beppe Mar 07 '11 at 10:05
0

Finally I was able to solve the problem thanks to GSerg and David Heffernan help.

Here the IDL to be used to generate the .tlb

[
    uuid(12345678-1234-1234-1234-123456789ABC),
    version(1.0)
] 

    library myTypeLib
    {
        [dllname("myLib.dll")]

        module myLib
        { 

            [entry("myFunc")]
            int __stdcall myFunc( LPSTR  filename_in,  LPSTR   filename_out, LPSTR  ErrMsg);
        };
    };

To compile it use the command "midl" in the Visual Studio command prompt.

The resulting .tlb file should be placed in the same directory of the VB6 project, together with the DLL.

In the VB6 project under Project->References it's possible to add the .tlb file.

If all is gone well, pressing F2, would be possible to notice "myTypeLib" in the list of the available library.

Now it's possible to call "myFunc" inside the VB6 project!

However there are two issue to point out:

1)Some variable types are not compatible between VB6 and C. An example of this issue is rapresented by char arrays. While in VB6 they are declared as Dim myStr as String, in C they are usually declared as char myStr[MAX_DIM];. To make possible the translation between VB6 and C, without modifing the DLL, it's possible to declare on VB6 side the strings as Dim myStr as String * 256, while in the IDL file the corrispondent string should be passed to the function as LPSTR myStr.

2)VB6 does not link the DLLs until the .exe is created. But if a DLL is not linked, then its functions are not visible. For this reasons, all the function of the implicitly linked DLLs that have to be used in the VB6 project, must be included in the IDL file.

Moreover, for the same reason, even after all the functions have been included in the IDL file, won't be possible to run the program from the IDE (it will crash) as to debug it. The only way to run the application is to create the .exe.

Beppe
  • 381
  • 2
  • 7
  • 15
  • That's strange, everything should work without creating the exe, and you should be able to debug, too. The only case when you can not debug is using a different calling convention in the tlb, such as `__cdecl`. I suggest you download the famous [Win32.tlb](http://webspace.webring.com/people/lp/practicalvb/vb/download/win32.html) and play with it -- you'll see everything just works. To extract the IDL source out of tlb, you can use any appropriate tool, such as [PE Browse](http://www.smidgeonsoft.prohosting.com/pebrowse-pro-file-viewer.html). – GSerg Mar 17 '11 at 13:01
  • Though the only difference I can spot is `__stdcall` vs `_stdcall`. – GSerg Mar 17 '11 at 13:08
  • @GSerg I tried to fix the difference you spotted, but it still crashes in the IDE. About the Windows API tlb, do u know if it includes any function that uses `__declspec(thread)` variables? – Beppe Mar 18 '11 at 10:48
  • Ah. I guess that counts as a different calling convention, too. Bugger. – GSerg Mar 18 '11 at 13:41