Saturday, July 22, 2006

SNTT - the Win32 API series - FindWindow and LotusScript

FindWindow is a Windows API call that gives you the handle to a specific window. For example, you can use FindWindow to get the instance of Excel then bring it to the front, resize it, close it, or any number of other things. A window handle is a good thing to be able to get.

The typical declare statement for FindWindow looks like this:

Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

The first parameter is the class of the window you want to get. For Notes it's just "Notes", but for Microsoft applications things get a little crazy. If you don't care which instance of an application you get you can just pass a placeholder for the parameter you don't care about. For example, to get the Notes window from VB6 or prior you would call it like this:

hWnd = FindWindow("Notes", vbNullString)

vbNullString is a constant that evaluates to "", so I thought I could simply use the following from LotusScript:

hWnd = FindWindow("Notes", "")

That doesn't work. After extensive searching, and help from friends in the Efnet IRC channel C#, I learned that the VB runtime is actually passing 0 when you use vbNullString. There's a lot going on with marshalling and LPSTR and other stuff I don't fully understand, but the upshot is that to get this working from Notes you have to change the declaration:

Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As Long) As Long

And then you can do:

hWnd = FindWindow("Notes", 0)

Okay, so if you know the class that's great, but finding the class can be a bit of a pain. What if you do know the exact window title? Then you have to declare it with the class as Long and the window name as String:

Declare Function FindWindowByClass Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As Long) As Long
Declare Function FindWindowByTitle Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Long, ByVal lpWindowName As String) As Long

Crazy, I know. I'm not sure if the fault is LotusScript's inability to pass pointers or Microsoft's API implementation. But this should help anyone trying to use FindWindow from LotusScript.


  1. i didn't you have to blame anyone. It's how C works.

    C use always a pointer to a string, because it didn't know what a string ist. (Windows and its API is written in C, like the most operation-systems). A pointer is a long-value thats points to a memory-address.

    The definition of the api-call is (and as it was developed only C was in the discussion), that you have to give a Null-Pointer. (Thats in this case a long-value that points to zero).

    Ok. It would be a nice extension for Lotus Script, when there would be a predefined Null-Pointer string, to get rid of dublicate declarations :)


  2. Rock'n. That's just what I was trying to track down. Thanks!!