VB6 dll call to win32 C function - winapi

I need to write some functions in C for someone else's VB6 project (that being outdated is beyond the scope of this question).
During initial tests, I could not get the calls to work. I have supplied a .def file, I tried to use __declspec(dllexport), stdcall and WINAPI calling conventions. Each call I get an error message in VB6 saying "bad dll calling convention."
Win32 C function prototypes:
long WINAPI BitmapFile_Open(char *pszFileName);
void WINAPI BitmapFile_Close(long bmf);
note in the above I have tried several other calling conventions, including __declspec(dllexport) and stdcall, and neither work.
Def file:
LIBRARY ImageLib
EXPORTS
BitmapFile_Open #1
BitmapFile_Open #2
VB Global Module:
Declare Function BitmapFile_Open Lib "ImageLib.dll" (ByVal fileName As String) As Long
Declare Function BitmapFile_Close Lib "ImageLib.dll" (ByVal bmFile As Long)
VB Code:
Dim myFile As Long
myFile = BitmapFile_Open("test.bmp")
BitmapFile_Close (myFile)
Also note that in the original functions, the bmFile is actually an address (pointer to a structure) but in VB it will be represented as long. However, since VB6 doesn't support pointers, I am casting from long in the C code. I hope you can understand what I'm trying to get at here. It has nothing to do with the error that is occurring. Any help is appreciated.
Edit: I have used a dependency walker to determine that the functions are indeed being exported. VB6 is just not calling them without error.

BitmapFile_Close should be declared as a Sub in the VB6. I can't see anything else wrong.
Look at the Microsoft advice on writing C DLLs to be called from VB. Originally released with VB5 but still relevant to VB6.

Try removing the ByVal from the arguments in question (inside the declaration section) one by one, then test and try removing for all arguments then test again. Do the incremental tests and report back if you can. That should do the trick!

Use MIDL to generate a type library for your DLL, then VB6 can use its type information instead of Declare Function routines.
For global functions, I seem to recall that you want a library and module defined.
See VB - Linking a DLL in implicit way

Related

how can i create dylib with init function

I am trying to create a dylib in xcode. I can able to create dylb by choosing c/c++ Library template in Xcode.
I want to add "init" method for this dylib. I don't know how to add "init" method for dylib.
My idea is to call this "init" on runtime with the help of dlopen().
Thanks for your valuable feedback.
If you code in C++, you could have static objects in your dlopen-ed library; their constructors get called at dlopen time (and their destructor is running at dlclose time).
If your code is compiled by gcc (be it in C, or in C++, or perhaps even some other languages) you could use the constructor and destructor function attributes
(You could use the obsolete symbols _init and _fini but this is an obsolete feature of dlopen (at least on Linux, and probably on MacOSX). Then you would have to declare them extern "C" void _init(void); in C++.)
Don't forget that dlsym deals with unmangled names, so you want to declare extern "C" the C++ names for it.
You could also have your own convention that your dynamically loaded things should have, for example, a function named my_initialization and your code doing the dlopen would later use dlsym to find it. You should have documented conventions on what symbols are dlsym-ed and how they are used.
I don't know well MacOSX, but I googled this documentation

Best way to know if a folder has specified file types

I have a loop procedure in VB6 which explores all the folders from a specified file path.
I then need to know if each detected folder contains MP3 files.
I don't want to use the dir command because it takes up a lot of resources.
I've tried doing this using FSO, APIs, etc, but I can't find a solution.
Thanks for any help.
VB6's Dir$() function is a pretty light wrapper on FindFirstFile and friends. I'm not sure why you think the FSO would be any lighter or faster.
The biggest serious limitations of Dir$() are that it is an ANSI function and it cannot be "interrupted" by a second search while one is already in progress without resetting the state of the first search.
What does "takes up a lot of resources" mean anyway?
I posted a Class wrapping the process at DirLister lightweight Dir() wrapper.
Have you tried the FindFirstFile API function? It should be your best shot. There's a C# example at codeproject A Faster Directory Enumerator
The VB signature goes like this:
<DllImport("kernel32.dll", CharSet := CharSet.Auto)> _
Private Shared Function FindFirstFile(ByVal lpFileName As String, ByRef lpFindFileData As WIN32_FIND_DATA) As IntPtr
End Function
Here's a sample VB implementation http://www.ask-4it.com/how-to-use-findfirstfile-win32-api-from-visual-basic-code-2-ca.html
You can also find a nice microsoft article on usage of the API here.

Import WinAPI Function in *.VBS File

Using visual basic in say Excel, I am able to declare WinAPI functions using the DECLARE keyword - e.g.
Declare Function SetLocaleInfo Lib "kernel32" Alias "SetLocaleInfoA" ( _
ByVal Locale As Long,
ByVal LCType As Long,
ByVal lpLCData As String
) As Boolean
However when using this syntax in a *.VBS file - it fails with a compile error.
Can anyone tell me what I need to do to run WinAPI functions from *.VBS files?
You can't run WinAPI functions from VBScript without some extra third-party support.
I believe there used to be vendor of a COM component which allowed VBScript to call into a standard dll but I can't remember what it was called and its quite possible that my imagination is playing tricks on me.
Plenty of forum posts mention 'dynacall' as the wrapper I think you're talking about however the two main links they post seem defunct
For what it's worth HTA may be worth looking at as an easy to use substitute
http://www.microsoft.com/downloads/en/details.aspx?FamilyId=231D8143-F21B-4707-B583-AE7B9152E6D9&displaylang=en

How to use dll's in the same directory as an excel file

This is somewhat related to my other question.
I've been using a dll to acompany an excel spreadsheet. Everything is currently working with the dll and excel is using it just fine. But is it possible to specify that a dll resides in the same directory as the excel file when declaring functions?
Declare Sub FortranCall Lib "Fcall.dll" (r1 As Long, ByVal num As String)
Unfortunetly this doesn't work, I have to use something like:
Declare Sub FortranCall Lib "C:\temp\Fcall.dll" (r1 As Long, ByVal num As String)
This works, but is going to cause headaches when distributing to my office mates. Placing the dll in c:\windows\system32 etc. is not really an option either.
Here are three possibilities for dynamically loading/calling into DLLs from VBA, including links to relevant info and some sample code. Can't say I've ever had to use any of the solutions described there, but it seems like a reasonable exploration of the options in light of VBA's need for a static path.
Create a new module at runtime (you could import a .bas file from disk, no need to hard-code the module with string literals), using the VBIDE Extensibility API. Drawback: no compile-time validation; you'll need to use stringly-typed Application.Run calls to invoke it. Requires trusted programmatic access to the VBIDE API (i.e. you allow VBA to execute code that generates code that is then executed... like macro viruses do).
Use the LoadLibrary Win32 API... and now you've got pointers and addresses: this scary code (.zip download) is essentially a huge unmaintainable hack that uses assembly language to enable invoking the API functions by name. Looks like it only works for a subset of supported Win32 API functions though.
Change the DLL search path, but then that also requires dynamic code added at run-time, so might as well go with the above.
Here's another potential solution that suggests programmatically updating the PATH environment variable prior to calling into your DLL. Not a bad idea, if it works, as you could add this to you workbook open event.
Good luck!
The way I generally take care of this is by adding:
Dim CurrentPath As String
CurrentPath = CurDir()
ChDir (ThisWorkbook.Path)
To: Private Sub Workbook_Open()
ChDir() should do the trick. It might not work on network folders though.
Declare Sub FortranCall Lib "Fcall.dll" (r1 As Long, ByVal num As String)
...
Dim CurrentPath As String
CurrentPath = CurDir()
ChDir (ThisWorkbook.Path)
Call FortranCall(r, n)
ChDir (CurrentPath) ' Change back to original directory
Now keep your .dll in the same folder as your workbook.
You can put the DLL in some directory and add it to the path EnVar.
ActiveWorkbook.Path gives you the full path to the folder containing the currently-active workbook. So try this:
Declare Sub FortranCall Lib ActiveWorkbook.Path & "\Fcall.dll" (r1 As Long, ByVal num As String)

Using GetProcAddress when the name might be decorated

What is the correct way to use GetProcAddress() on a 32 bit DLL? On win32, there are three calling conventions, cdecl, stdcall and fastcall. If the function in the DLL is foo they will decorate the name in the following ways _foo, _foo#N and #foo#N.
But if the author of the dll has used a .def file, then the exported name will be changed to just "foo" without any decoration.
This spells trouble for me because if I want to load foo from a dll that is using stdcall, should I use the decorated name:
void *h = LoadLibraryEx(L"foo.dll", NULL, 0);
GetProcAddres((HMODULE)h, L"_foo#16");
Or the undecorated one:
void *h = LoadLibraryEx(L"foo.dll", NULL, 0);
GetProcAddres((HMODULE)h, L"foo");
? Should I guess? I've looked at lots of 32 bit DLL files (stdcall and cdecl) and they all seem to export the undecorated name. But can you assume that is always the case?
There's really no shortcut or definitive rule here. You have to know the name of the function. The normal scenario is that you know at compile time the name of the function. In which case it does not matter whether the exported name is mangled, decorated, or indeed completely unrelated to the semantic name. Functions can be exported without names, by ordinal. Again, you need to know how the function was exported.
If you are presented with a header file for a library, and want to link to it with explicit linking (LoadLibrary/GetProcAddress) then you will need to find out the names of the function. Use a tool like dumpbin or Dependency Walker to do that.
Now, the other scenario which might lead to you asking the question is that you don't know the name at compile time. For instance, the name is provided by the user of your program in one way or another. Again, it is quite reasonable to require the user to know the exported name of the function.
Finally, you can parse the PE meta data for the executable file to enumerate its exported function. This will give you a list of exported function names, and exported function ordinals. This is what tools like dumpbin and Dependency Walker do.
If __declspec(dllexport) is used during compilation and __declspec(dllimport) in header file, as well as extern "c", then you do not need to decorate those functions. The __declspec helps in using the undecorated names, but function overloads, namespaces, and classes still require same way to distinguish them.
Usually, object oriented functions are exported using function ordinals instead of their decorated names. Cast the ordinal as (char*)(unsigned short)ordinal, thus, GetProcAddress(module, (char*)(unsigned short)ordinal);
Edit: while most of Windows use UTF-16, GetProcAddress uses UTF-8, so it cannot use a wide character string.
GetProcAddress(module, L"foo") is identical to GetProcAddress(module, "f");

Resources