WMLiberty Primer

© 2003, Brent Thorne

Home

Password Textbox API

Character Replacement

LB Isam Library

Beginning Games 2

Rubber Band Objects

WMLiberty Primer

LB Browser

Beginning Programming 5

INPUTTO Demo

Chase Button

Questionaire Wizard

MIDI Output Thoughts

MIDI-Tunes

Play MIDI DLL

Directory Search Function

Newsletter help

Index

Introduction

Brent Thorn has graciously granted me permission to reprint a part of a thread which he posted on the API forum of the Liberty Basic board. It explains the use of WMLiberty a little bit in relation to a question which was raised on June 1st. The explanation comes with some great code. Note that in order to use the code you will have to download the wmliberty package from the FTP site where it is archived: [http://babek.info/libertybasicfiles/lbf_menu.html]

Before we get into Brent explanation, lets set the context a little bit by checking out a snippet of requestor's question. In his message he is seeking some guidance on trapping system messages and passing that message information back to Liberty Basic. Lets take a look at what is being asked...

When a window is restored, Windows sends a message called EVENT_SYSTEM_MINIMIZEEND.

Alyce says that my program can receive this message with the help of wmliberty.dll, which I've downloaded from

[http://babek.info/libertybasicfiles/lbf_menu.html]

The information in the file inside the zip, plus the LB Help for CALLDLL and CALLBACK, give me a general idea of what I need to do, but it's still fuzzy.

The file "About WMLiberty.txt" says:

Quote:

SetWMHandler subclasses a window and sets up a callback function to receive a single window message.

CallDLL #WMLiberty, "SetWMHandler", _

hWnd As long, _

uMsg As long, _

lpfnCallback As long, _

lSuccess As long, _

lResult As long

+ hWnd is a handle to a window, usually a top-level window.

So I need to use the LB HWND() function to get the handle of my window *before* executing CALLDLL ?

Quote:

+ lpfnCallback is a pointer to a callback function. The callback should be defined like this:

Callback lpfnCallback, Handler( long, long, long, long ), long

Is this literally what the line of code should look like in my program? My guess is that I can change the name of the function ("Handler", in this case) to anything I like, but everything else on the line should be exactly as shown. Right?

Quote:

The handler function usually looks like this:

Function Handler( hWnd, uMsg, wParam, lParam )

So these two are identical to whatever I have in CALLDLL?

Quote:

wParam, lParam have different meanings for different messages. Refer to the documentation for the message for details.

The info I find for EVENT_SYSTEM_MINIMIZEEND does not say anything about *any* parameters, that I can see.

[http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msaa/msaaccrf_7jlf.asp]

Bigger questions:

Do I call WMLiberty just once, or in a loop? If called just once, what should I put after the call? a WAIT command? What is the purpose of the callback function?

The Response by Brent:

WMLiberty is a tool for "power users" to trap events, sent as messages, that LB does not enable us to trap natively.

Therefore, the first thing you need to determine is the Windows Message that Windows sends or posts to a window to tell that window of the event. Most messages start with a WM_ prefix, which we must prepend an underscore for LB to recognize it (_WM_).

By reading the documentation for the message, we can find out other information we need to know, like if the message is sent to a child control or its parent window, what the wParam and lParam mean to the message, what we should return to indicate successful processing, and whether returning that means the message will stop there or be forwarded to a higher level.

From experience I know the WM_SIZE message is sent to a top-level window when its size or restore/minimize state changes. The EVENT_*'s you found are something totally different. If you look at the top of the frame, you will see where this page lies within the documentation hierarchy. I clicked on the "Active Accessibility" link and came to a page that defines this. Active Accessibility is a COM (Component Object Model) interface. COM is the base for such technologies as ActiveX and DirectX. LB cannot yet do COM.

Looking at the documentation for WM_SIZE, we are given the meanings of wParam and lParam. lParam holds the new size of the window's client area in the high and low words, but we don't need to know this, so we'll just ignore lParam. wParam is what we're interested in; it holds a value that indicates the type of sizing that just took place.

You are interested in catching when a window is restored from a minimized state, which trapping the WM_SIZE message will let you do. We would compare wParam to the constant _SIZE_RESTORED. However, it does not tell us if the window was restored from a minimized state or a maximized state. Additionally, if the user can maximize the window from a minimized state, wParam will not equal _SIZE_RESTORED. How complicated this gets depends on the type of window you are using. (Remember that for graphics and text windows the HWnd() function returns the embedded graphicbox or texteditor.) If the window can be maximized, I would suggest testing for wParam <> _SIZE_MINIMIZED.

I wrote a simple demo that beeps whenever the window is restored.

Code:

NoMainWin

Open "WM_SIZE Demo" For Window As #1
#1 "TrapClose [Quit]"

Open "WMLiberty.dll" For DLL As #wmlib
hWnd = HWnd(#1)
Callback lpfnCallback, OnSize(long, long, long, long), long
CallDLL #wmlib, "SetWMHandler", _
hWnd As long, _
_WM_SIZE As long, _
lpfnCallback As long, _
0 As long, _ 'lSuccess
r As long
[Loop]
Scan
CallDLL #kernel32, "Sleep", _
100 As long, _
r As void
GoTo [Loop]
[Quit]
Close #1
CLose #wmlib
End

Function OnSize( hWnd, uMsg, wParam, lParam )
OnSize = 1 'always forward
If wParam = _SIZE_RESTORED Then
Beep
End If
End Function

I'll point out some highlights to answer some of your other questions.

Callback lpfnCallback, OnSize(long, long, long, long), long

This returns a pointer to the OnSize() function in the variable lpfnCallback. I usually name message handlers by replacing the WM_ with On and "titlizing" the remainder (e.g. WM_SETTINGCHANGE to OnSettingChange). This naming convention is very common and is used for a similar purpose as ours in Visual C++.

0 As long, _ 'lSuccess

The reference tells us that we indicate successful processing by returning zero. When OnSize() returns, WMLiberty checks it against the value that we told it means success. If they match WMLiberty does not forward the message for default processing.

r As long

In WMLiberty's documentation this is named lResult. It should equal zero. But if we passed SetWMHandler some bad information, r would indicate which parameter is bad (i.e. 1 = bad hWnd, 2 = bad uMsg, 3 = bad lpfnCallback, etc.). It is appropriate to check this value, but like most programmers, I only check return values when something does not work.

Scan

When using WMLiberty is necessary to use a Scan loop. For reasons, known only by Carl, LB gets confused when it returns to a Wait following a callback function. The TrapClose event is affected and the window cannot be closed properly. The call to Sleep is a way to reduce CPU usage.

OnSize = 1 'always forward

This sets the return value for the OnSize() function to one. If you recall, we told WMLiberty success is zero. However, we always return non-zero because we want the message to go on for default processing. Some strange behavior can result from not forwarding certain messages.

On some other questions, you seem to be confused by the redundancy I put into the callback function; its needing a hWnd and uMsg parameters, which are the same is those passed to SetWMHandler. This is to allow the same callback function to be reused for multiple windows and/or messages. If you subclass more than one window/control and want the same kinds of processing done to each, this can save your typing a lot more redundant code.

Follow-up

Brent later added a few more comments as they related to some requests for clarification. The comments are excellent and add to the discussion above:

It doesn't really matter where you open a DLL in your code, just as long as it's open when you try to CallDLL. WMLiberty catches when a window is destroyed and releases its hooks from it then. You must keep WMLiberty open while a window is subclassed by it. You should note that WMLiberty is still experimental. I have started rewriting it several times, but other obligations always supercede.

The reason for using a function is that callbacks require them. WMLiberty calls your code when it receives a message you wanted it to catch. Of course, the same scope rules apply for callback function as for regular functions and subs. If your program stops a timer when it is minimized, the callback can restart it. You can also check a global variable in the scan loop. You might need more than one scan loop. Code that takes advantage of WMLiberty is not always the most intuitive or readable around, but it usually get a job done.

Readability also comes into the reasons we use named constants instead of the values they represent. When we come back to a chunk of code after many months or years, how are we to know what a plain number means to a function that might have eight parameters passed to it. Humans find names to be easier to remember and less error-prone than numbers or symbols.


Home

Password Textbox API

Character Replacement

LB Isam Library

Beginning Games 2

Rubber Band Objects

WMLiberty Primer

LB Browser

Beginning Programming 5

INPUTTO Demo

Chase Button

Questionaire Wizard

MIDI Output Thoughts

MIDI-Tunes

Play MIDI DLL

Directory Search Function

Newsletter help

Index