Loading DLL Libraries in Code, Crashes Application?

Here’s a Windows ONLY demo of an example. Any idea how to prevent the crash after the library has been successfully used?

Function LoadLib(libraryName as String, subroutineName as String, p1 as integer, p2 as String, p3 as integer, p4 as integer) As Integer
  Dim library, address as integer
  dim mb,mb2 as memoryBlock
  dim rk1,rk2 as string
  dim result as integer
  
  Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA"(lpLibFileName As Ptr) As Integer
  Declare Function GetProcAddress Lib "kernel32" (hModule As Integer,lpProcName As Ptr) As Integer
  Declare Function CallWindowProc Lib "user32" Alias"CallWindowProcA" (lpPrevWndFunc as Integer, hwnd as integer, msg as WString, wParam as integer, lParam as integer) as Integer
  Declare Function FreeLibrary Lib "kernel32" (hLibModule As Integer)As Integer
  
  // for LoadLibrary
  mb = new MemoryBlock(lenB(libraryName)+1)
  mb.cstring(0) = libraryName
  
  // for GetProcAddress
  mb2 = new MemoryBlock(lenB(subroutineName)+1)
  mb2.cstring(0) = subroutineName
  
  Dim mb3 as MemoryBlock
  mb3 = new MemoryBlock(LenB(p2)+1)
  mb3.CString(0) = p2
  
  library = LoadLibrary(mb)
  if library = 0 then
    MsgBox "I'm sorry, I couldn't link to the library " + libraryName
    return -1
  end if
  
  address = GetProcAddress(library, mb2)
  if address = 0 then
    MsgBox "I'm sorry, I couldn't link to the procedure" + subroutineName + " in library " + libraryName
    return -1
  end if
  
  result = CallWindowProc(address, p1, mb3.CString(0), p3, p4)
  
  Call FreeLibrary(library)
  
  return result
End Function

To test the method, place a pushbutton on a window and in its Action event place:

Dim x as integer = loadlib("user32","SetWindowTextA",self.Handle,"hello world",0,0)

I know that the crash is a BEX crash, which is a buffer overflow exception… so is the memoryblock shorter than the actual buffer!!! :-/

[quote=189849:@Matthew Combatti] Declare Function LoadLibrary Lib “kernel32” Alias “LoadLibraryA”(lpLibFileName As Ptr) As Integer
Declare Function GetProcAddress Lib “kernel32” (hModule As Integer,lpProcName As Ptr) As Integer
[/quote]

The names here should be declared as CString.

I now have this…and it lasts “longer” but still crashes. :frowning:

Function LoadLib(libraryName as String, subroutineName as String, p1 as integer, p2 as String, p3 as integer, p4 as integer) As Integer
  Dim library, address as integer
  dim mb,mb2 as memoryBlock
  dim rk1,rk2 as string
  dim result as integer
  
  Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA"(lpLibFileName As CString) As Integer
  Declare Function GetProcAddress Lib "kernel32" (hModule As Integer,lpProcName As CString) As Integer
  Declare Function CallWindowProc Lib "user32" Alias"CallWindowProcA" (lpPrevWndFunc as Integer, hwnd as integer, msg as CString, wParam as integer, lParam as integer) as Integer
  Declare Function FreeLibrary Lib "kernel32" (hLibModule As Integer)As Integer
  
  // for LoadLibrary
  mb = new MemoryBlock(lenB(libraryName)+1)
  mb.cstring(0) = libraryName
  
  // for GetProcAddress
  mb2 = new MemoryBlock(lenB(subroutineName)+1)
  mb2.cstring(0) = subroutineName
   
  library = LoadLibrary(mb)
  if library = 0 then
    MsgBox "I'm sorry, I couldn't link to the library " + libraryName
    return -1
  end if
  
  address = GetProcAddress(library, mb2)
  if address = 0 then
    MsgBox "I'm sorry, I couldn't link to the procedure" + subroutineName + " in library " + libraryName
    return -1
  end if
  
  result = CallWindowProc(address, p1, p2, p3, p4)
  
  Call FreeLibrary(library)
  
  return result
End Function

[quote=189856:@Matthew Combatti] // for LoadLibrary
mb = new MemoryBlock(lenB(libraryName)+1)
mb.cstring(0) = libraryName

// for GetProcAddress
mb2 = new MemoryBlock(lenB(subroutineName)+1)
mb2.cstring(0) = subroutineName
[/quote]

You don’t need MemoryBlocks here. Just pass in the strings and they’ll convert to CString.

CallWindowProc can’t be used to execute arbitrary function pointers. It expects the function pointed to by the address parameter to match a specific signature. Passing a pointer to a function with a different signature (such as SetWindowTextA) will crash the application.

After Joe’s Suggestion, I’m finally down to:

Function LoadLib(libraryName as String, subroutineName as String, p1 as integer, p2 as String, p3 as integer, p4 as integer) As Integer
  Dim library, address as integer
  dim rk1,rk2 as string
  dim result as integer
  
  Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA"(lpLibFileName As CString) As Integer
  Declare Function GetProcAddress Lib "kernel32" (hModule As Integer,lpProcName As CString) As Integer
  Declare Function CallWindowProc Lib "user32" Alias"CallWindowProcA" (lpPrevWndFunc as Integer, hwnd as integer, msg as CString, wParam as integer, lParam as integer) as Integer
  Declare Function FreeLibrary Lib "kernel32" (hLibModule As Integer)As Integer
  
  library = LoadLibrary(libraryName)
  if library = 0 then
    MsgBox "I'm sorry, I couldn't link to the library " + libraryName
    return -1
  end if
  
  address = GetProcAddress(library, subroutineName)
  if address = 0 then
    MsgBox "I'm sorry, I couldn't link to the procedure" + subroutineName + " in library " + libraryName
    return -1
  end if
  
  result = CallWindowProc(address, p1, p2, p3, p4)
  
  Call FreeLibrary(library)
  
  return result
End Function

To ensure the 32-bit User32 library is used, which contains “SetWindowTitleA”, I use…

call loadlib("c:\\windows\\system32\\User32.dll","SetWindowTextA",self.Handle,"hello world",0,0)

So how do we call the library function? Without the crash?

Thanks for the info Andrew… hopefully there has to be a simple solution, as this is how it would be done in C++ or even VB6 with no crashing :-/

Ok…did a bit of memory peeking with a debugger and I guess its back to the drawing board… Xojo is passing 4 parameters instead of 2 to the SetWindowTextA method… so the stack crashes…

[quote=189860:@Matthew Combatti]
So how do we call the library function? Without the crash?[/quote]

You can use the Ptr from GetProcAddress to construct a delegate that matches the function signature, and then call Delegate.Invoke. However, this requires that the function signature must be known at compile-time. (If someone knows a way to invoke a delegate without knowing its parameters then I’d love to see it.)

System.IsFunctionAvailable is just a wrapper for LoadLibrary and GetProcAddress. It doesn’t return a function pointer, but it will load the library.

I think I can build a psuedo-delagate in memory using copymem,some api and direct bytecode (in-memory assembly)… It should get around the data execution BEX error…I’ll be back with a rough-draft reusable class. Thanks for the help thus-far guys :slight_smile:

Dynamically loading a DLL function…

Ok, so I have worked 2 different methods out, the first is module based and pure Xojo API calls by Declares. This works great, ONLY IF AND WHEN an API you are invoking has 4 parameters. (This is the method seen above…) So, GetDC(hdcparam) wouldn’t work, SetWindowTextA wouldn’t work, and 90% of the system API calls. Unacceptable method…

Method 2: Still crashes, but is close to working as I have transposed the code to MS Visual Studio C++/C#/VB and it works perfectly. Xojo is the troublesome language to get it right. Method 2 creates the bytecode directly from the function calls and parameters, then throws it on the stack, but the app crashes as the call is added to the stack. The rest of the stack manipulation works perfectly (all of it works flawlessly everywhere else, except Xojo at the moment)…only the one line …

AddCallToCode (m_lpFn)

found in the PrepareCode method, brings it all crashing down. I’d like to get this class working since, unlike
Xojo’s API/Declares, can load dynamic system APIs that are not limited to MUST HAVING 4 parameters.

Here’s the new source code:
http://www.xojodevspot.com/demos/API_and_ByteCode.xojo_binary_project

Any further ideas to get it working would be greatly appreciated.

Just curious, what exactly does this do for you that soft declares don’t? What’s the benefit?

Looks like you’re trying to copy memory to address 0 from a negative address
in copyMemory, change

Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As integer, lpSource As Integer, cBytes As Integer) to

Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (byref lpDest As integer, byref lpSource As Integer, cBytes As Integer)

or, if I understand what you’re doing there,

Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As ptr, byref lpSource As Integer, cBytes As Integer) and use a memoryblock instead of an array in addLongToCode.

Still doesn’t work, but should get you a little closer maybe…

It can DYNAMICALLY load API at RUNTIME without a limit and requirement of 4 parameters…currently, Xojo only allows dynamically loaded API at runtime with a requirement of 4 parameters (No more, no less…as seen in the first module method included in the project posted). I need to dynamically invoke a library method since:

  1. After installing the client software, the security protocol does not allow a software update to the primary application. (Technically it is possible, but A LOT of configuration changes and re-setup is required (followed by an ‘inspection’) after any changes to the primary application that loads when the system boots. (I didn’t make the security method which is absolutely foolish IMO.)

  2. By dynamically loading API from a DLL library, we can update the libraries associated with the primary application without re-installing and re-configuring/setting-up the system all over again. Just drop in the new updated libraries and loading script, and update complete…no hassles.

  3. Soft Declares/Declares MUST know the name of the Library and Function BEFORE compilation, and cannot be modified or altered at runtime. Soft Declares do load libraries at Runtime, but the name must still be known prior to compilation. In our case, the library/function name may change after compilation, so this will NEED to be accessible and changeable.

@jim mckay - AH! I will try that. I attempted neglecting the MemoryBlock after removing MemoryBlocks from the original code above. A MemoryBlock makes much more sense than an array. I was thinking in terms of Xojo’s COM.SafeArray… was a long day :slight_smile: Thanks.

Will be back…

I’ve been tinkering and I’ve got something that works, at least for simple tests like SetWindowTextA. I’ve put it up on Github.

Example that works on my machine:

  Dim proc As DynamicInvoke.Invoker ' the object that wraps the Ptr
  proc = DynamicInvoke.GetProcAddress("User32", "SetWindowTextA")
  Dim caption As CString = "Hello, world!!" ' You must use CStrings or WStrings explicitly
  Dim value As Integer = proc.Invoke(Window1.Handle, caption) ' set window1's caption

Great stuff Andrew, you always post the most interesting stuff.

Thanks!