Page 1 of 1
Need an unsupported API lesson
Posted: Fri Apr 30, 2021 12:22 pm
by BarryG
So I've come to realize that I really need to learn how to use unsupported API calls in my apps so I don't need to keep bothering people here, hehe.
Here's an example that doesn't work. As far as I know, I should be using ProtoTypes instead of CallFunction, but they don't make sense to me. So can someone please convert this to a ProtoType so I have it as a code template for converting other unsupported API calls in future? Thank you.
Code: Select all
; https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowdisplayaffinity
#WDA_NONE=0
#WDA_MONITOR=1
Sleep_(2000) ; Allow time for user to select a target window.
hWnd=GetForegroundWindow_()
If OpenLibrary(0,"user32.dll") And GetFunction(0,"SetWindowDisplayAffinity")
Debug CallFunction(0,"SetWindowDisplayAffinity",hWnd,#WDA_MONITOR) ; Returns 0
CloseLibrary(0)
EndIf
Re: Need an unsupported API lesson
Posted: Fri Apr 30, 2021 12:51 pm
by Mijikai
If possible dont use prototypes.
Code: Select all
EnableExplicit
Import "User32.lib";<- Lib from the MS SDK
SetWindowDisplayAffinity.b(hWnd.i,dwAffinity.l)
EndImport
;...
End
Anway prototypes are super simple.
It just doesnt make sense to go the manual way if you can properly import the function/s.
Code: Select all
EnableExplicit
Prototype.b API_SetWindowDisplayAffinity(hWnd.i,dwAffinity.l)
Global *SetWindowDisplayAffinity.API_SetWindowDisplayAffinity
;...
;*SetWindowDisplayAffinity = GetFunction(#Library, FunctionName$)
;...
End
Re: Need an unsupported API lesson
Posted: Sat May 01, 2021 2:55 am
by BarryG
Mijikai wrote: Fri Apr 30, 2021 12:51 pm
If possible dont use prototypes.
But the manual says to use ProtoTypes instead of CallFunction; as have several other people in these forums before. So now I'm confused.
Also, I can't get your examples to work. Can you post a working example to teach me a practical lesson so I can learn? This doesn't compile:
Code: Select all
#WDA_NONE=0
#WDA_MONITOR=1
Import "user32.lib"
SetWindowDisplayAffinity.b(hWnd.i,dwAffinity.l)
EndImport
Sleep_(2000) ; Allow time for user to select a target window.
hWnd=GetForegroundWindow_()
SetWindowDisplayAffinity(hWnd,#WDA_MONITOR)
Re: Need an unsupported API lesson
Posted: Sat May 01, 2021 8:25 am
by fryquez
Your function is properly failing because you don't have the rights or don't own this window.
Code: Select all
; https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowdisplayaffinity
#WDA_NONE=0
#WDA_MONITOR=1
Prototype pSetWindowDisplayAffinity(hWnd, dwAffinity)
Global SetWindowDisplayAffinity.pSetWindowDisplayAffinity
If OpenLibrary(0,"user32.dll")
SetWindowDisplayAffinity = GetFunction(0,"SetWindowDisplayAffinity")
CloseLibrary(0) ; user32 can be closed, don't do this with other dlls
EndIf
If OpenWindow(0, 0, 0, 400, 100, "SetWindowDisplayAffinity Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ButtonGadget(0, 10, 10, 380, 20, "SetWindowDisplayAffinity")
Repeat
Event = WaitWindowEvent()
Select event
Case #PB_Event_Gadget
Select EventGadget()
Case 0
If SetWindowDisplayAffinity
Debug SetWindowDisplayAffinity(WindowID(0),#WDA_MONITOR)
EndIf
EndSelect
EndSelect
Until Event = #PB_Event_CloseWindow
EndIf
Re: Need an unsupported API lesson
Posted: Sat May 01, 2021 8:40 am
by BarryG
Thanks fryquez, that works for my window and compiles now.
But I still don't understand the code behind ProtoTypes, or how they work. I want to understand it.
This line in your code is the key:
Code: Select all
SetWindowDisplayAffinity = GetFunction(0,"SetWindowDisplayAffinity")
But this seems to be just setting a variable value? So how does that relate to this call:
Code: Select all
SetWindowDisplayAffinity(WindowID(0),#WDA_MONITOR)
Since when does calling a variable do something? None of this makes any sense to me. Please ELI5.
Re: Need an unsupported API lesson
Posted: Sat May 01, 2021 8:49 am
by fryquez
Yeah, GetFunction() saves the function address in your variable SetWindowDisplayAffinity.
And that variable we have previously declared as Prototype:
Code: Select all
Prototype pSetWindowDisplayAffinity(hWnd, dwAffinity)
Global SetWindowDisplayAffinity.pSetWindowDisplayAffinity
The compiler now allows you to call that function address with your declared 2 parameters.
Re: Need an unsupported API lesson
Posted: Sat May 01, 2021 9:03 am
by Demivec
@BarryG:
Prototypes for functions are similar to Structures for data types. They both define a way to interpret things for an address. Structures start at an address and detail how information is organized and how it is interpreted. A Structure does nothing until it is applied to data at a given address. You do this by creating Structured variables or Structured pointer variables.
Prototypes define a functions parameters, their types, default parameters and return type. A Prototype does nothing until it is applied to the address of a function. You do this by creating a Prototype variable (i.e. var.prototype_name) and then assigning it the address of a function. You can get the address of the function in several ways. Afterwards you call the function using the Prototype variable along with the parameters of the function (i.e. var(a, b, c)).
Here are ways to get the address of a function:
Code: Select all
;if getting the address of a function from a DLL or library
SetWindowDisplayAffinity = GetFunction(0,"SetWindowDisplayAffinity")
;if getting the address of one of your own functions
MyPrototypeVar = @myFunc()
Re: Need an unsupported API lesson
Posted: Sat May 01, 2021 12:36 pm
by ChrisR
I've used it once or twice, without really understanding how it works so I was following the topic.
It's much clearer now. Thanks fryquez, Demivec for explanations
Re: Need an unsupported API lesson
Posted: Sat May 01, 2021 8:04 pm
by Mijikai
BarryG wrote: Sat May 01, 2021 2:55 am
Mijikai wrote: Fri Apr 30, 2021 12:51 pm
If possible dont use prototypes.
But the manual says to use ProtoTypes instead of CallFunction; as have several other people in these forums before. So now I'm confused.
...
Yes, but no one should use any of those options if there is a way to properly import the functions.
BarryG wrote: Sat May 01, 2021 2:55 am
Also, I can't get your examples to work. Can you post a working example to teach me a practical lesson so I can learn? This doesn't compile:
...
1. Lookup the function and the required *.lib through MSDN.
2. Download Microsofts SDK to get access to the *.lib.
3. Use Import to import the functions (see PB help)
The problem is that PureBasics *.lib versions are stipped down for compatibility (sometimes you might get lucky).
So check them first and then move on to the official ones if needed.
There might be cases where it is more complicated.
Either use prototypes or do more research on how to Import functions of that specific *.lib.
Usually there is always a solution and those problems are really not common especially if we talk about OS Apis.
Re: Need an unsupported API lesson
Posted: Sun May 02, 2021 12:31 am
by BarryG
Thanks everyone! It's much clearer to me now, and I think I'll be able to import other unsupported APIs after seeing the examples here.
Re: Need an unsupported API lesson
Posted: Sun May 02, 2021 1:27 pm
by chi
I agree, you should use Import over Prototype when calling an API function that's not available with PB out of the box, but if you use a function on a daily bases, it makes more sense to me to actually import (pbcompiler.exe /Import ...) the function by creating an .imp file. Sometimes you have to replace the lib file, though... As Mijikai already stated, the PB libs are kinda stripped down (Libs that shipped with WinXP, I think). The only time I use Prototypes is, when I need to call a function that is only supported on newer versions of Windows. SetWindowDisplayAffinity is already a good example... It is only available with Windows 7 (and up) and will crash your program when you call the Import function on e.g. Vista or XP since it can't find the entry in the dll. Using Prototype gives you the possibility to call a dummy function instead, which will not crash your program...
Code: Select all
Procedure _SetWindowDisplayAffinity(hWnd, dwAffinity) : EndProcedure
Prototype _SetWindowDisplayAffinity(hWnd, dwAffinity)
Global SetWindowDisplayAffinity__._SetWindowDisplayAffinity
user32 = OpenLibrary(#PB_Any, "user32")
If user32
SetWindowDisplayAffinity__ = GetFunction(user32, "SetWindowDisplayAffinity")
If SetWindowDisplayAffinity__ = 0 : SetWindowDisplayAffinity__ = @_SetWindowDisplayAffinity() : EndIf
Else
SetWindowDisplayAffinity__ = @_SetWindowDisplayAffinity()
EndIf
OpenWindow(0, 0, 0, 320, 200, "", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ButtonGadget(0, 10, 10, 100, 30, "Button")
SetWindowDisplayAffinity__(WindowID(0), #True)
While WaitWindowEvent() <> #PB_Event_CloseWindow : Wend
If IsLibrary(user32)
CloseLibrary(user32)
EndIf