Solved

Calling a windows DLL function

Posted on 2000-05-04
7
215 Views
Last Modified: 2010-05-02
I have been sent a DLL and a snippet of C code that calls into its function.

I try to port this into Visual Basic 6.0 without much success.

Sample source will be appreciated

Here is the snippet for the C code if it is useful
if (LoadLibrary("ETIWRD32.DLL"))!= NULL)
{              
  lpFunc = (lpFunction) (GetProcAddress(hLibrary, (LPSTR) "ConvertFile"));
  if (lpFunc != NULL)
  {
    iRes = (*lpFunc)((LPSTR) "aaa.doc", (LPSTR) "aaa.txt", (FARPROC) NULL);
  }
}
0
Comment
Question by:crooz21
7 Comments
 
LVL 2

Expert Comment

by:BobbyOwens
ID: 2779728
You need to declare the API first.

Declare Function GetProcAddress Lib "kernel32" Alias "GetProcAddress" (ByVal hModule As Long, ByVal lpProcName As String) As Long

The API takes the handle to the module and the name of the process. (function)

To get the handle to the module, either use LoadLibrady or getModuleHandle

Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long

OR

Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long

Below is an example using the functions:

SUMMARY
The version of DCOM95 or DCOM98 installed on a Windows 9x system may be determined by checking the registry using Visual Basic. If DCOM is installed on the system, the default value of the registry key:

HKCR\CLSID\{bdc67890-4fc0-11d0-a805-00aa006d2ea4}\InstalledVersion

will contain the version of DCOM installed on the system. The version number in the registry is stored in the format "a,b,c,d", where a, b, c, and d are numeric values. These values form the version number of the DCOM install:

InstalledVersion DCOM Version
"4,71,0,3328" DCOM95 and DCOM98 1.3 4.71.0.3328
"4,71,0,2618" DCOM95 1.2 Web Release 4.71.0.2618
"4,71,0,2612" DCOM 1.2 4.71.0.2612 DCOM98.exe shipped with VS6.0
"4,71,0,1719" DCOM Win98 Gold 4.71.0.1719
"4,71,0,1718" DCOM95 1.1 4.71.0.1718
"4,71,0,1120" DCOM 1.x 4.71.0.1120




MORE INFORMATION
The following steps outline this process from Visual Basic:

Start a new Visual Basic Standard EXE project. Form1 is created by default.


Add a CommandButton (Command1) to Form1.


Add the following code to the General Declarations section of Form1:



Option Explicit

Private Const ERROR_SUCCESS = 0&

Private Const HKEY_LOCAL_MACHINE = &H80000002
Private Const HKEY_CLASSES_ROOT = &H80000000

Private Const STANDARD_RIGHTS_ALL = &H1F0000
Private Const KEY_QUERY_VALUE = &H1
Private Const KEY_SET_VALUE = &H2
Private Const KEY_CREATE_SUB_KEY = &H4
Private Const KEY_ENUMERATE_SUB_KEYS = &H8
Private Const KEY_NOTIFY = &H10
Private Const KEY_CREATE_LINK = &H20
Private Const SYNCHRONIZE = &H100000

Private Const KEY_ALL_ACCESS = _
   ((STANDARD_RIGHTS_ALL Or _
     KEY_QUERY_VALUE Or _
     KEY_SET_VALUE Or _
     KEY_CREATE_SUB_KEY Or _
     KEY_ENUMERATE_SUB_KEYS Or _
     KEY_NOTIFY Or _
     KEY_CREATE_LINK _
    ) _
    And (Not SYNCHRONIZE) _
   )

Private Declare Function RegOpenKeyEx _
   Lib "advapi32.dll" Alias "RegOpenKeyExA" _
   (ByVal hKey As Long, _
    ByVal lpSubKey As String, _
    ByVal ulOptions As Long, _
    ByVal samDesired As Long, _
    phkResult As Long) _
   As Long

Private Declare Function RegCloseKey Lib _
   "advapi32.dll" _
   (ByVal hKey As Long) _
   As Long

Private Declare Function RegQueryValueEx Lib _
   "advapi32.dll" Alias "RegQueryValueExA" _
   (ByVal hKey As Long, _
    ByVal lpValueName As String, _
    ByVal lpReserved As Long, _
    lpType As Long, _
    ByVal lpData As String, _
    lpcbData As Long) _
   As Long
   
' Note that if you declare the lpData
' parameter as String, you must pass it
' By Value as is done here.

Private Declare Function GetProcAddress _
   Lib "kernel32" _
   (ByVal hModule As Long, _
    ByVal lpProcName As String) _
   As Long

Private Declare Function GetModuleHandle _
   Lib "kernel32" Alias "GetModuleHandleA" _
   (ByVal lpModuleName As String) _
   As Long
   
Private Function DCOMEnabled() As Boolean
' We need to check two things
' 1- OLE32 supports free threading
' 2- DCOM is enabled in the registry
' See Inside COM, Rogerson, pp 276-277

   DCOMEnabled = False
   
' First, check to see if OLE32 supports free threading.
' You need to check for the CoInitializeEx function's
' presence in OLE32.
' 1- Get a handle to the OLE32 module
' 2- Try to get a ProcAddress for CoInitializeEx

   Dim OLE32ModuleHandle As Long
   Dim CoInitializeExProcAddress As Long
   
   OLE32ModuleHandle = GetModuleHandle("OLE32")
   Debug.Assert (Not OLE32ModuleHandle = 0)
   
   CoInitializeExProcAddress = GetProcAddress( _
           OLE32ModuleHandle, _
           "CoInitializeEx")
         
   Debug.Print "CoInitializeExProcAddress = " _
               & CoInitializeExProcAddress
               
   If CoInitializeExProcAddress = 0 Then
      DCOMEnabled = False
      Exit Function
   End If
   
' Now check the registry to see if DCOM is enabled.
   
   Dim lResult As Long
   Dim hKey As Long

   lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _
                   "SOFTWARE\Microsoft\Ole", _
                   0, _
                   KEY_ALL_ACCESS, _
                   hKey)
   
   Debug.Assert (lResult = ERROR_SUCCESS)

   Dim rgch As String
   rgch = String(2, 0)
   
   Dim cbrgch As Long
   cbrgch = Len(rgch)
   
   lResult = RegQueryValueEx(hKey, "EnableDCOM", 0, 0&, rgch, cbrgch)

   Debug.Assert (lResult = ERROR_SUCCESS)

   Debug.Print "Mid$(rgch, 1) is " & Mid$(rgch, 1)

   If (Mid$(rgch, 1, 1) = "Y" Or Mid$(rgch, 1, 1) = "y") Then
      ' DCOM is Enabled
      DCOMEnabled = True
   Else
      DCOMEnabled = False
   End If
                   
   lResult = RegCloseKey(hKey)
           
End Function

Public Function GetDCOMVersion() As String
    ' Check the registry key "InstalledVersion" to see which
    ' version of DCOM is installed.

   Dim hKey As Long
   Dim lResult As Long

' First confirm that DCOM is installed
   If Not DCOMEnabled() Then
       MsgBox "Can't check version.  DCOM is not installed."
       Exit Function
   End If

' open the the proper registry key
   lResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, _
         "CLSID\{bdc67890-4fc0-11d0-a805-00aa006d2ea4}\InstalledVersion", _
         0, _
         KEY_ALL_ACCESS, _
         hKey)

   If Not lResult = ERROR_SUCCESS Then
       MsgBox "Could not open registry key"
       Exit Function
   End If

   Dim rgch As String
   rgch = String(64, 0)

   Dim cbrgch As Long
   cbrgch = Len(rgch)

   lResult = RegQueryValueEx(hKey, "", 0, 0&, rgch, cbrgch)

   lResult = RegCloseKey(hKey)

   Dim temp As String

   temp = Mid$(rgch, 1, cbrgch)

   GetDCOMVersion = temp

End Function

Private Sub Command1_Click()
   MsgBox GetDCOMVersion()
End Sub
Run the program and click Command1.

Result: If DCOM is installed on the system, the message box will display the version number.
Notes:

The function DCOMEnabled may be used separately to determine if DCOM has been installed on a system without checking for the version.





0
 

Author Comment

by:crooz21
ID: 2779911
Bobby,

You have suggested a long way.

I am trying to get it done in a simpler way, here is the sample code, and the error, please tell me what is wrong here

=============================
Private Declare Function ConvertFileA Lib "ETIWRD32.DLL" _
Alias "ConvertFile" (ByVal InputFile As String, ByVal OutputFile As String,
ByVal Callback As Long) As Long
Private Sub Form_Load()
    ConvertFileA "aaa.doc", "aaa.txt", 0
End Sub
===============================

The convert was called, but an error pops up when the function call
exits, here is the error message
Run-time error '49':
Bad DLL calling convention

Cheers



0
 
LVL 2

Expert Comment

by:BobbyOwens
ID: 2780167
Are you sure of the syntax of the DLL.

The error number you describe is when the paramaters are incorrect.
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 5

Expert Comment

by:KDivad
ID: 2780172
I have absolutely no idea if it will help, but you could try passing vbNull as the last parameter instead of 0...
0
 
LVL 2

Accepted Solution

by:
BobbyOwens earned 50 total points
ID: 2780179
I've just found this:

It is not possible to directly call a C function in a DLL if that function uses the _cdecl calling convention. This is because Visual Basic uses the _stdcall calling convention for calling functions. This is a problem because if _cdecl is used, the calling function is responsible for cleaning up the stack. However, if _stdcall is used, the called function is responsible for cleaning up the stack.

NOTE: An .EXE file created in Visual Basic will allow you to call a DLL function that has been declared with the _cdecl calling convention without an error. It is only when you try to call such a function when running a program from the Visual Basic IDE, that Visual Basic generates the following error:


Run-time Error '49':
Bad DLL Calling Convention


You could try passing a variable as the last parameter instead of an actual number
0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 2780184
The calling convention is something that I don't believe you are going to be able to do anything about in VB. Essentially it is the way that registers get called and who handles clean up of memory the DLL or the calling application. Most better programmers use a convention called STDCALL (which is available to almost all development environments), but occasionally a C or C++ programmer will use CDECL. As far as I know VB makes no allowance for this convention. I know that STDCALL works, since UI use it all the time with Delphi and VB.


Good luck!!
0
 

Author Comment

by:crooz21
ID: 2784982
Bobby,

Your are almost right. I compiled the VB into .EXE and it runs ok. But it works if compiled with No Optimization.

Note: vbNull doesn't help.

Thanks to other guys too.
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
MS Date Picker 64 bit 32 bit issue 12 65
Visual Studio search word table and return Cell index 8 78
Add and format columns in vb6 7 72
backup program with robocopy 6 75
When designing a form there are several BorderStyles to choose from, all of which can be classified as either 'Fixed' or 'Sizable' and I'd guess that 'Fixed Single' or one of the other fixed types is the most popular choice. I assume it's the most p…
Article by: Martin
Here are a few simple, working, games that you can use as-is or as the basis for your own games. Tic-Tac-Toe This is one of the simplest of all games.   The game allows for a choice of who goes first and keeps track of the number of wins for…
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…
This lesson covers basic error handling code in Microsoft Excel using VBA. This is the first lesson in a 3-part series that uses code to loop through an Excel spreadsheet in VBA and then fix errors, taking advantage of error handling code. This l…

730 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question