Page 1 of 1

Calling a windows function from purebasic

Posted: Wed Mar 24, 2021 3:46 pm
by dangerfreak
My OS is Windows 10 (64 bit) and I want to call a windows library function via purebasic itself and with the built-in assembler. It works perfectly using the 32bit compiler of Purebasic, but not with the 64bit compiler.

This is the code (it's just an example for the windows "Beep" function):

Code: Select all

EnableExplicit

#lib=0
Global *address

If OpenLibrary(#lib,"kernel32.dll")=0
   Debug("dll not found")
   End
EndIf

ExamineLibraryFunctions(#lib)
While NextLibraryFunction()
   If LibraryFunctionName()="Beep"
      *address=LibraryFunctionAddress()
      Break
   EndIf
Wend
CloseLibrary(#lib)

If *address=0
   Debug("address not found")
   End
EndIf

Debug("calling from purebasic - works with 32 and 64bit compiler")
CallFunctionFast(*address,100,500) ; works!

Delay(1000)

Debug("calling from assembler - only works with 32 bit compiler")
!push 500 ; beep len in ms
!push 200 ; beep frequency
!CALL [p_address]

Delay(1000)
End
What could be the reason? Any ideas?

PLEASE: Don't ask for the "why". Just tell me, what is wrong with the code. Thank you very much.

Re: Calling a windows function from purebasic

Posted: Wed Mar 24, 2021 5:01 pm
by netmaestro
First and foremost, CallFunctionFast is crushingly limited in every way you can imagine. It was written before Prototypes and Imports were available in PureBasic. Please forget you ever learned of it. I hereby release you from any and all responsibilities, real and imagined, related to your discovery of this profoundly deprecated function. I show two correct implementations:

Code: Select all

; Using Prototypes

OpenLibrary(0, "kernel32.dll")
Prototype Beep(frequency.l, duration.l)
Global Beepit.Beep = GetFunction(0, "Beep")

Beepit(500,500)

CloseLibrary(0)

; Using Imports

Import "" ; when it's kernel32 that's all you need but you'd normally code Import "kernel32.lib" or such
  Beep(frequency.l, duration.l)
EndImport

Beep(500,500)
I'm not sure of the implementation in asm, you could read the generated asm from my examples and see what's there. Alternatively you could ask Wilbert or Helle and you'd get a comprehensive answer pretty quick. Those two are amazing asm coders. Idle too. (if I forgot someone, sorry about that. At least you know I'm not mad at you or I'd remember your name :D .)

Re: Calling a windows function from purebasic

Posted: Wed Mar 24, 2021 5:11 pm
by Jeff8888
Note Windows X64 uses registers for first four subroutine parameters, so this fix to your code works.

Debug("calling from assembler - only works with 32 bit compiler")
!mov rdx,500 ; beep len in ms
!mov rcx,800 ; beep frequency
!CALL [p_address]


See Microsoft doc: https://docs.microsoft.com/en-us/cpp/bu ... w=msvc-160

Re: Calling a windows function from purebasic

Posted: Wed Mar 24, 2021 5:19 pm
by RASHAD
From the previous posts of the fellows

Code: Select all

EnableExplicit

#lib=0
Global *address

If OpenLibrary(#lib,"kernel32.dll")=0
   Debug("dll not found")
   End
EndIf

*address = GetFunction(#lib, "Beep")

CloseLibrary(#lib)

If *address=0
   Debug("address not found")
   End
EndIf

Debug("calling from purebasic - works with 32 and 64bit compiler")
CallFunctionFast(*address,100,500) ; works!

Delay(1000)

Debug("calling from assembler - only works with 32 bit compiler")
CompilerIf #PB_Compiler_Processor  = #PB_Processor_x86
!push 500 ; beep len in ms
!push 200 ; beep frequency
!CALL [p_address]
CompilerElse
!mov rdx,500 ; beep len in ms
!mov rcx,200 ; beep frequency
!CALL [p_address]
CompilerEndIf

Delay(1000)
End

Re: Calling a windows function from purebasic

Posted: Wed Mar 24, 2021 7:10 pm
by dangerfreak
Thanks @all, that really helped me a lot!

There's one question left, maybe you also know an answer for it. The following code uses windows library calls to find the address of a function (function "Beep" again). It works perfectly with the 32bit compiler, but not with the 64bit compiler (the result is 0):

Code: Select all

EnableExplicit

Global hwnd.l
Global *string
Global *address

; Get handle to library:
hwnd=GetModuleHandle_("kernel32.dll")

; Convert the string "beep" from unicode to ascii:
; (sorry, I don't know an easier way)
*string=AllocateMemory(100)
PokeS(*string,"Beep",-1,#PB_Ascii)

; get the function address - only works with the 32bit compiler of Purebasic
*address=GetProcAddress_(hwnd,*string) ; this function only works with an ascii/ansi string

Debug(*address)
If *address=0
   Debug("Couldn't find function 'Beep'")
EndIf

FreeMemory(*string)
Any ideas? And is there an easier way to convert an unicode string to ascii/ansi?

Re: Calling a windows function from purebasic

Posted: Wed Mar 24, 2021 7:47 pm
by StarBootics
dangerfreak wrote:And is there an easier way to convert an unicode string to ascii/ansi?
I have stop using Window in 2009 so I can't help you with your issue. But the easiest way to create an Ascii buffer is like that

Code: Select all

*String = Ascii("Beep")

; Do your stuff with the *String buffer

FreeMemory(*String)
THe FreeMemory() call is important to avoid a Memory leak.

Best regards
StarBootics

Re: Calling a windows function from purebasic

Posted: Wed Mar 24, 2021 7:48 pm
by RASHAD
For x86 & x64

Code: Select all

EnableExplicit

Global hwnd.i
Global *string
Global *address

; Get handle to library:
hwnd = GetModuleHandle_("kernel32.dll")

; Convert the string "beep" from unicode to ascii:
; (sorry, I don't know an easier way)
*string = Ascii("Beep")

; get the function address - only works with the 32bit compiler of Purebasic
*address=GetProcAddress_(hwnd,*string) ; this function only works with an ascii/ansi string

Debug(*address)
If *address=0
   Debug("Couldn't find function 'Beep'")
EndIf

FreeMemory(*string)
For x86

Code: Select all

EnableExplicit

Global hwnd.l
Global *string
Global *address

; Get handle to library:
hwnd = GetModuleHandle_("kernel32.dll")

; Convert the string "beep" from unicode to ascii:
; (sorry, I don't know an easier way)
*string = Ascii("Beep")

; get the function address - only works with the 32bit compiler of Purebasic
*address=GetProcAddress_(hwnd,*string) ; this function only works with an ascii/ansi string

Debug(*address)
If *address=0
   Debug("Couldn't find function 'Beep'")
EndIf

FreeMemory(*string)
For x64

Code: Select all

EnableExplicit

Global *hwnd
Global *String
Global *address

; Get handle to library:
*hwnd = GetModuleHandle_("kernel32.dll")
*String = Ascii("Beep")

; Convert the string "beep" from unicode to ascii:
; (sorry, I don't know an easier way)
;*string=AllocateMemory(100)
;PokeS(*string,"Beep",-1,#PB_Ascii)


; get the function address - only works with the 32bit compiler of Purebasic
*address=GetProcAddress_(*hwnd,*String) ; this function only works with an ascii/ansi string

Debug(*address)
If *address=0
   Debug("Couldn't find function 'Beep'")
EndIf

FreeMemory(*String)

Re: Calling a windows function from purebasic

Posted: Mon Apr 19, 2021 12:43 pm
by dangerfreak
Thanks for all your help, that solved my problem! :-)))

PS: Sorry for my late answer, I had some trouble with the forum.

Re: Calling a windows function from purebasic

Posted: Mon Apr 19, 2021 1:47 pm
by infratec
@Rashad
To avoid the string conversion use Import:

Code: Select all

EnableExplicit

Define hwnd.i
Define *address

Import ""
  GetProcAddress.i(hwnd.i, string.p-ascii)
EndImport

; Get handle to library:
hwnd = GetModuleHandle_("kernel32.dll")

; get the function address - only works with the 32bit compiler of Purebasic
*address=GetProcAddress(hwnd, "Beep") ; this function only works with an ascii/ansi string

Debug(*address)
If *address=0
   Debug("Couldn't find function 'Beep'")
EndIf

Re: Calling a windows function from purebasic

Posted: Mon Apr 19, 2021 6:52 pm
by RASHAD
Hi infratec
I was answering dangerfreak for how to convert Unicode to Ascii using PB :wink:
So he can use it anytime with registered dll or not
Or any other purpose
Thanks anyway