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
[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]
I now have this…and it lasts “longer” but still crashes.
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
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.
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…
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
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.
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:
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.)
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.
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 Thanks.
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