Sie sind auf Seite 1von 17

Making Goofy Windows with LabVIEW

An Intro to the Windows API for LabVIEW Programmers

Introduction

The main goal of this exercise is to demonstrate how to call Windows dlls, which opens up the
domain of “real” Windows programming to LabVIEW developers. Most of us, myself included,
don’t really have much exposure to Windows programming during the normal course of our
work, and most of the Windows tutorials out there are targeted at C# and C++ developers
(Microsoft’s workhorse languages), so hopefully this tutorial will provide a bit of exposure, and
show that you don’t have to use Visual Studio to create GUIs with a richness on par with what
Visual Studio can produce. However, because the main goal is to introduce Windows
programming I don’t really cover at all the operation of the LabVIEW portions of the example
code. I’m assuming that you know how LabVIEW works, I didn’t do anything particularly tricky
with the LabVIEW code, and I documented it well (I hope), so hopefully you won’t have too
much trouble deciphering how it works on your own. If, on the other hand, you are not familiar
with LabVIEW programming, then you may want to learn LabVIEW before tackling this
tutorial. I’m not expecting you to be a LabVIEW expert by any means, but when I say, for
example, that a particular node is on the functions palette, I’m expecting you to know what I’m
talking about.

I tried to provide just enough information to allow you to get a goofy shaped window up and
running as quickly and painlessly as possible, and not much more information than that. This
means that I skimped on some of the details on what the functions and parameters are “really”
doing. If you’re interested in finding more out about this, then you should check the MSDN site
for a more full explanation. In every case I tried to use the same parameter, function, and dll
names as are used by Microsoft, so a quick search should turn them up.

Finally, and most importantly, I wrote and tested the example code on Windows 7 64-bit. If you
are running it on another platform, then when you open it, LabVIEW will complain that it can’t
find one, and maybe both, dlls used, so you’ll have to re-link them. They should be in your
system32 folder if they are not in your SysWOW64 folder. I cover this later in the tutorial in
more detail, I just wanted to get it out here for those of you who are just going to open the
example without reading the tutorial (like I probably would). It goes without saying (hopefully)
that this tutorial only applies to Windows programming, so if you want to make goofy windows
on a Mac, or something else, you’re on your own.

Alright then, everybody ready?

On with the Show

Tired of rectangular windows? Want something a little goofier? Well, you’re in luck; your PC
comes with a pair of dlls that will help you fulfill all your goofy windows desires. It’s as easy as
1, 2, 3:

Step 1: Acquire a handle to the window you want to make goofy.


Step 2: Create a goofy region from an array of POINT structs.

Step 3: Set the goofy region as defined in step 2 for the window whose handle was
acquired in step 1.

Clear? I didn’t think so. Allow me to explain.

LabVIEW doesn’t have the ability to do any of these things on its own, not directly anyway. We
are entering the dark and nebulous world of Windows programming, and all of these things
require calls to functions located deep within the bowels of various (i.e. 2) Windows dlls. So the
first thing we have to cover is using the Call Library Function Node in LabVIEW. I’m sure
you’re all pros at that, right?

Call Library Function Node? Whatchu talkin’ ‘bout?

The Call Library Function Node is what is used in LabVIEW to call a function located inside a
dll. You can find it on the functions palette here:
It is the node with the little books on it (Get it? Books? Library?). I’ve used this little sucker for
calling C and C++ dlls many times, and it’s never failed me. Debugging can be difficult as the
code is already compiled, but I’m sure this will work for you bug-free the first time you run it, so
let’s not worry about that. So now that we’ve found it, let’s drop one on the ol’ block diagram.

Once you have one on the black diagram double click it, and a dialog box pops up. It should look
like so:
First thing, we’re going to want to select the dll to call. The first one we’re going to need is the
user32.dll. So click the browse button (the little folder) next to Library name or Path, and browse
to it. If you are using a 32 bit system it should be in the “[root]\Windows\System32” folder. If
you are using a 64 bit system it should be in “[root]\Windows\SysWOW64”. I’ll wait here till
you find it.

Found it? Good. Moving on.

The dll we just selected is packed with functions that we can now access. The “Function name”
drop-down box should be populated with all of them. As per step 1 above, we want to select the
function that will allow us to get a handle to (on?) our window. So click the little arrow, and find
the function named “FindWindowA”, and select it. Under “Thread” leave “Run in UI thread”
selected. For the “calling convention”, select “stdcall (WINAPI)”. I don’t want to get into calling
conventions except to say that if you’re calling a win32 API function (which we are), then you
want to use the “stdcall (WINAPI)” calling convention.

Alright so now our dialog box ought to look like so:


The next step is specifying the parameters, which is the trickiest part of this whole exercise. I’ll
tell you how I determined how they should be set up after we actually do it. I want you to be able
to see what they look like in LabVIEW first, so just bear with me for a minute, and in the next
section I’ll show you how the sausage is actually made.

Click the “Parameters” tab; we’re going to specify our parameters. The screen should look like
this:
Parameters are what we feed into the function, and return type is what the function returns after it
completes. A function can have several parameters, but only one return type. LabVIEW can’t tell
how many parameters a function needs, so we have to tell it. In this case, the FindWindowA
function needs 2 parameters, so press that blue plus sign a couple times to add a couple of
parameters to the list. You can name the parameters anything you want as long as they are of the
right type, but if you want them to agree with the MSDN documentation name the return
parameter (the one named “return type” by default) to “HWND”, the first parameter (arg1)
“lpClassName”, and the second parameter (arg2) “lpWindowName”. Now set the types as
follows (you can select these from the appropriate drop down boxes):

Name: HWND
Type: Numeric
Data Type: Unsigned Pointer-sized Integer

Name: lpClassName
Type: Numeric
Constant: not checked
Data Type: Unsigned Pointer- sized Integer
Pass: Value

Name: lpWindowName
Type: String
Constant: checked
Data Type: C String Pointer
Minimum size:<None>

After all that, the dialog box should look like this:

And I bet you thought there were no pointers in LabVIEW

So what to do if you don’t know what parameters a function needs, and you don’t have a tutorial
to tell you? Go to the MSDN site, and look it up. MSDN provides descriptions of the function
prototypes for every function on the list. The prototype will normally be near the top of the
MSDN page that describes the function written in a box labeled “Syntax.” The prototype on the
MSDN site should look very similar to the “Function prototype” in the above dialog box. They
are not, however, often identical. For instance, the “FindWindowA” function points to a page for
a function named “FindWindow”, which provides the following prototype.

HWND FindWindow(
LPCTSTR lpClassName,
LPCTSTR lpWindowName
);

Which at first glance doesn’t look anything like our prototype, so what gives?

As far as where the “A” at the end of the name, it appears Microsoft left out some rather
important information on their page, or, more likely, the page was just out of date during the
writing of this tutorial. There is actually no function named “FindWindow”, there are in fact two
functions named “FindWindowA”, and “FindWindowW”. They do the same thing but the “W”
function uses Unicode strings in the parameters, and the “A” version uses ASCII strings.
Unicode strings are wider (i.e. contain more bits per character), hence the “W”. LabVIEW can
only use ASCII strings as far as I know, so we use the “A” version. I do seem to remember
something about LabVIEW maybe incorporating Unicode strings in the future, but unless you
hear a good reason to believe otherwise, assume ASCII. Also, I’m told that if a dll function
returns a Unicode string you can probably use it as a parameter in another dll function, but if you
try to view it it’s going to look really funny as all the characters will be truncated at the eight bit
mark.

Now, let’s look at the return type. Ours is an unsigned pointer-sized integer, and the MSDN
version is just called HWND. Looking up HWND on MSDN reveals that it is a typedef of type
HANDLE, which is in turn a typedef of type PVOID, which is a pointer, so we select unsigned
pointer-sized integer, which is LabVIEW’s more exact way of describing what is normally (in
the C++ world) just called “pointer”. This isn’t exactly true. If we really want to be pedantic,
HWND is a window handle, not a pointer. A handle is an opaque pointer, which is a little
different, but for our purposes, just assume they are the same.

In versions of LabVIEW before 8.6, there was no pointer-sized integer, so you would’ve had to
select unsigned integer. However 64-bit systems use 64-bit pointers, and 32-bit systems use 32-
bit pointers, so selecting an unsigned 32-bit integer would have possibly caused problems on a
64-bit machine, so the good folks at NI provided us with this new data type, the pointer-sized
integer. The pointer-sized integer knows what size a pointer is on your system, and uses that size
automatically, so we don’t have to worry about it. You’ll probably notice that there is also a
signed pointer-sized integer. This is exactly the same as the unsigned pointer-sized integer except
that it lets you wire a signed instead of an unsigned integer to this input.

The first parameter is a bit tricky, so hold on tight. The MSDN page that describes the
FindWindow function tells us straight out that the first parameter type, LPCTSTR, is a pointer to
a string, so normally we would pass in a string as we do with the third parameter, but we want
the value to be NULL for reasons which will be described later. A NULL pointer is a pointer that
has a value of 0, which means it doesn’t point at anything. This is fundamentally different than
pointing at an empty string, so if you pass in an empty string, the value of the pointer won’t be
NULL, the value of the pointer will be the address of the empty string, and the dll will just get
confused and not work. Therefore, we want to use the pointer-sized integer data type again, and
will, later on, wire in a zero.

The third and last parameter, like the second one, is a pointer to a string, but this time we actually
want to use a string, so we select “String” for our type instead of “Numeric”. The dlls can’t
handle passing or returning strings like LabVIEW can. They can only handle pointers to strings
as specified on the MSDN site. LabVIEW provides several different types of string pointers
depending on the type of the dll we are calling. In our case we want to use “C String Pointer”.
Now we can just wire a LabVIEW string to this parameter when we are ready, and the node will
know what to do.
So that’s how you specify parameters. The other two tabs in the dialog box can just be left alone.
Press OK, were done. Well, we’re done with this library call anyway, and step 1 is now (mostly)
out of the way. The other two steps are similar in that for each one we will be calling dlls using
Call Library Function Nodes, and setting up the options, so rather than go through it in detail, I’ll
just summarize.

So the only tricky thing is the redraw parameter in the third dll. MSDN specifies a Boolean data
type, but LabVIEW doesn’t provide that option. However, a Boolean is just an integer where 0 =
false, and all other values are true. It is often constrained to 0=false and 1=true. We can therefore
just wire in an unsigned integer, which is what I did.

Call Library Function Node 2:

dll name: gdi32.dll


Function name: CreatePolygonRgn
Thread: Run in UI Thread
Calling convention: stdcall(WINAPI)

Return type:
Name: HRGN
Type: Numeric
Data type: Unsigned Pointer-sized Integer

Parameters:
Parameter 1:
Name: lppt
Type: Adapt to Type
Constant: not checked
Data format: Array Data Pointer

Parameter 2:
Name: cPoints
Type: Numeric
Constant: not checked
Data format: Signed 32-bit Integer
Pass: Value

Parameter 3:
Name: fnPolyFillMode
Type: Numeric
Constant: not checked
Data format: Signed 32-bit Integer
Pass: Value

Call Library Function Node 3:


dll name: user32.dll
Function name: SetWindowRgn
Thread: Run in UI Thread
Calling convention: stdcall(WINAPI)

Return type:
Name: HRGN
Type: Numeric
Data type: Unsigned Pointer-sized Integer

Parameters:
Parameter 1:
Name: HWND
Type: Numeric
Constant: not checked
Data type: Unsigned Pointer-sized Integer
Pass: Value

Parameter 2:
Name: HRGN
Type: Numeric
Constant: not checked
Data type: Unsigned Pointer-sized Integer
Pass: Value

Parameter 3:
Name: bRedraw
Type: Numeric
Constant: not checked
Data format: Signed 32-bit Integer
Pass: Value

Wowser, that was a lot of work. So now your block diagram should look something like this.
Make sure you have the right number of parameters (the inputs on the left-hand side of the
nodes), and that they are the right type. Do the same for the return types (the upper right output
on the nodes). Also show the labels and make sure the dll and function names are correct, some
of them are very similar.
If your block diagram looks like the above, then the worst is over. All we have to do is specify
the parameters, and decide what to do with the return types. If you just want to gitter done, and
don’t really care about how any of this works (like how you felt about your college core
curriculum), just skip ahead to the next picture, make your block diagram look like it, and move
on. If you want to know more about what the heck the parameters and return types are actually
doing, then read on my nerdy friend.

The first library function call handles step 1 above, it gets a handle to our window. The first
parameter is the pointer that we discussed earlier. Now that we are back in traditional LabVIEW
programming land we have no pointer variables directly available, so just choose an unsigned
integer that is the right size. On 64-bit systems this will be a U64 on 32-bit systems this will be a
U32. If you’re not sure which one to use, just right click the input and select create constant, and
it will pick the right one for you. The second parameter is just a string; nothing fancy there. As
far as what values they need to be, here’s all you need to know (for this tutorial anyway): If the
first parameter is 0 (or NULL), and the second one is the name of a window (e.g. the name of a
VI), then the function will return a handle to the window named in the second parameter. For our
name, just retrieve the name of the VI whose block diagram we are working on.

The second library function call creates a windows region. The first parameter is a pointer to an
array of POINT structs. A struct in C and C++ is pretty much the same thing as a cluster in
LabVIEW, so we know we’re going to need an array of clusters. Now go check the MSDN site
to find out what is in the POINT struct. Oh, alright, I’ll just tell you. Put two I32s in there, the
first one named X, and the second one named Y. This array defines the outline of your goofy
window. The second parameter is the size of the array in the first parameter, easy enough. And
the third parameter effectively tells Windows whether to cut out the window or not (this isn’t
exactly true, but for our purposes it’s an accurate enough description, so let’s just pretend). So
pass in a 1 to clip the window, and a 0 to restore it.

The third function call is easy. The first parameter is the return type from the first function call.
The second parameter is the return type from the second function call. And the third parameter
tells Windows whether or not we want to redraw the window once it has been clipped. We
always do, so just hard code a 1 there.

Your block diagram should now look like this:

There are now just two things left to do before we can make a goofy window (we’re in the home
stretch, baby!). The first, is we must save the VI, otherwise it won’t work (it doesn’t matter
where or under what name, just so long as it’s saved), so go ahead and do that. The second thing
we have to do is specify points for the array.

The points start in the upper left corner of our front panel window, and, assuming you labeled
and ordered them properly in your cluster (you did didn’t you? Maybe you should check.), X
increases horizontally to the right, and Y increases vertically down. The simplest shape we can
cut out is a triangle, so let’s start with point X = 0 and Y = 0 (the upper left corner); then go to X
= 300, Y = 20; then to X = 30, Y = 300. We don’t have to close the shape as Windows will
automatically connect the last point to the first one, hence finishing the loop. So our block
diagram now looks like this:
Are you ready to rock? Let’s do it then. Run the VI, and your front panel should go from this:

To this:
If you want it to go back to normal, set fnPolyFillMode on the second function call to 0 (or false
as I have written it), and run it again.

As I said earlier, the array of points must specify a complete loop. We can’t specify one region,
and then another (not with this function anyway). So how do we make windows that are in 2
pieces like this?
Here is the array I used to create the above two piece window. Check it out, and see if you can
figure out what I did:

See it? I traced around the first rectangle, went out along a line (let’s call it the connecting line).
Around the second rectangle until I came to the end of the connecting line. Then back along the
connecting line until I reached the other end. Then I finished the first rectangle. Pretty clever,
huh? Alright, I didn’t come up with that on my own, but give me a little credit? I don’t ask for
much.

But Wait, there’s More

So this is all groovy (goofy?) and everything, but isn’t it a little arduous to have to figure out all
the points I need to make a really goofy (groovy?) window? That could take ages.
I’m glad you asked that question, because if you order now, I’ll throw in a bitmap mask creator
for free! That’s right, a zero dollar value that’s your free! OK, you don’t actually have to order
anything, just download it. It’s part of the example code that comes with this tutorial.

To use the mask creator just draw up a bitmap where the parts of the window you want cut out
are black (that is R, G, B = 0, 0, 0), and the parts you want to keep are white (or any other color
other than black). Oh, and the going out and coming back along the same line trick in order to
make a multi-piece window I showed you a few paragraphs ago? You can’t do that with the
bitmap mask creator. I mean it’s theoretically possibly, but I didn’t write that in, so if you really
want to use it to make multi-piece windows you’ll have to modify it on your own. But what you
can do is make a bitmap that looks like this:

And use it to make a window that looks like this:


That’s All, Folks

I’d like to give a big thanks to Jim Zurcher for helping out with some technical issues and editing
assistance.

If you have any questions, comments, complaints, ideas, or anything else, don’t hesitate to
contact me; I’d appreciate the feedback. You can find me here:

http://decibel.ni.com/content/groups/pacific-northwest

Das könnte Ihnen auch gefallen