Need an unsupported API lesson

Windows specific forum
BarryG
Addict
Addict
Posts: 3267
Joined: Thu Apr 18, 2019 8:17 am

Need an unsupported API lesson

Post 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
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: Need an unsupported API lesson

Post 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
BarryG
Addict
Addict
Posts: 3267
Joined: Thu Apr 18, 2019 8:17 am

Re: Need an unsupported API lesson

Post 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)
fryquez
Enthusiast
Enthusiast
Posts: 359
Joined: Mon Dec 21, 2015 8:12 pm

Re: Need an unsupported API lesson

Post 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
BarryG
Addict
Addict
Posts: 3267
Joined: Thu Apr 18, 2019 8:17 am

Re: Need an unsupported API lesson

Post 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.
fryquez
Enthusiast
Enthusiast
Posts: 359
Joined: Mon Dec 21, 2015 8:12 pm

Re: Need an unsupported API lesson

Post 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.
User avatar
Demivec
Addict
Addict
Posts: 4082
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Need an unsupported API lesson

Post 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()
User avatar
ChrisR
Addict
Addict
Posts: 1124
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: Need an unsupported API lesson

Post 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
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: Need an unsupported API lesson

Post 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.
BarryG
Addict
Addict
Posts: 3267
Joined: Thu Apr 18, 2019 8:17 am

Re: Need an unsupported API lesson

Post 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.
User avatar
chi
Addict
Addict
Posts: 1028
Joined: Sat May 05, 2007 5:31 pm
Location: Linz, Austria

Re: Need an unsupported API lesson

Post 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
Et cetera is my worst enemy
Post Reply