Hello and welcome to the second and final part of our API tutorial!
A good few years ago, I made one rather drastic mistake. I presumed.
Not just any old presumption, but a whopping great huge presumption that ended in unhappy users and unhappy bosses.
You see, I thought hey, my Windows directory is C:Windows – and therefore, that must be the case for every other computer lover in the entire world.
You see, Windows NT users typically have Windows in a directory called C:WinNT – as I learned. And some picky people opt to choose a different installation path such as C:Windoze – as I learned.
And yet others would go ahead and choose completely different drives to install the operating system, such as F:Windows
Still others would do both, such as J:WinDiddlyDonk – I hated those the most.
But hey, I was young and innocent as my users figured when the application crashed due to an incorrect path error. Oops. Still, it taught me never to presume in the wacky world of Windows. Instead, use the API.
<Ed groans: Why did I sense that was coming? >
The API provides a couple of groovy little functions that allow us to grab the main directories such as the Windows directory, System directory and Temporary directory. For the moment, we’re just going to look at GetWindowsDirectory:
- Load up the API Viewer
- Find and copy the following declarations: GetWindowsDirectory
- Create a new project in Visual Basic and paste the declaration into a module
Now this declaration is a little different to the ones we have uncovered so far. Why? Well, take a look at it:
Public Declare Function GetWindowsDirectory _ Lib "kernel32" _ Alias "GetWindowsDirectoryA" _ (ByVal lpBuffer As String, ByVal nSize As Long) As Long
Notice that the first parameter requires us to pass a string. We’ve not done that before and unfortunately it ain’t all that simple.
Why? Well, alas, most API DLLs were created in the C programming language. And that means peculiarities. For instance, when dealing with strings, the API adds an extra character to the end a null ‘terminator’ – so it knows where in memory the string stops.
But VB doesn’t use those, so when you get strings back from the API, you have to knock off that end terminator. Also, when you’re passing the actual string, you need to tell the API how big the string is so it doesn’t put too much information in.
You see, the API doesn’t really know what a string is. It talks in terms of memory. So if it tried to put more information in than it should, that extra info could run out of your string and into some other area of memory which could result in a system crash. So remember API calls can cause havoc – save your work regularly.
Anyway, we’ll see all of this and more by the end of this lesson. Worried yet? Don’t be. I’m here to help you. Promise. Let’s go:
- Add a Command Button to your Form
- Place the following code behind the button:
Private Sub Command1_Click() Dim strWinDir As String Dim lngSize As Long strWinDir = Space(255) lngSize = 255 Call GetWindowsDirectory(strWinDir, lngSize - 1) strWinDir = TrimNull(strWinDir) MsgBox strWinDirEnd Sub
Don’t run it straight away it won’t work properly. First, allow me to explain the code so far:
Dim strWinDir As StringDim lngSize As Long
You know this bit. We’re just declaring two variables, as required by the API call one a String, the other a Long.
strWinDir = Space(255)
Now this is an interesting piece of code. The Space function just returns a certain number of spaces, dependant on the number you pass to it. In this case, we sent 255 its way so the function returns 255 spaces.
Huh? So we’re setting our string to equal 255 spaces? What’s the bloomin’ point in that? Well, when we’re passing strings to the API, we need to ‘pad them out’ in other words, set aside 255 spaces in memory, reserved for our string.
Before we added these spaces, Visual Basic didn’t know how many characters the string would hold. And that’s fine, VB handles all that stuff dynamically. But C doesn’t. Oh no. Before you can use a string, C needs to know how much memory it has to work with so we pad it out with duff information to create a memory ‘buffer’.
lngSize = 255
This line records the size of that memory buffer. In this case, 255 characters worth. You see, when we call this API function, it asks for two things the string (strWinDir) and how much ‘memory’ in that string it can play with. We’re going to use lngSize to hold that information.
Call GetWindowsDirectory(strWinDir, lngSize - 1)
This is the actual API call. We pass the Windows directory string and the Long. Notice that we actually pass in lngSize minus 1, aka 254. Why? Well, remember I said C adds a null terminator to the end of its strings so it knows where they finish in memory?
Well, this is how we handle it. We’re saying "Hey API Guy, here’s the strWinDir string. You can use 254 characters of it"
And the API thinks to itself, "Sure, you think I’m only going to use 254 characters but I’m actually going to add one extra character on the end, a null terminator, just so I know where to stop. Haha!"
And we think, "I know what that evil API Guy is like. That’s why I actually passed it a string that has space to hold 255 characters in memory. But I only told the API to use a maximum of 254, to account for him naughtily adding the one-character terminator!"
Phew! And now the next line:
strWinDir = TrimNull(strWinDir)
This just calls a function I’ve written called ‘TrimNull’. It basically chops that extra terminator character off the end. We’ll add this function in a minute.
But hold on for two twitches of a ducks whisker! As the GetWindowsDirectory declaration shows, we’re passing strWinDir to the API ‘ByVal’. This means we’re passing the variable by value (which simply passes the string data) rather than by reference (which passes a pointer to the string variable).
If you were to typically pass the variable by value the thing you’re passing it to can’t change the original strWinDir variable. But if you pass by reference, the thing you’re passing it to can change the original.
Many of you will already be aware of this common Visual Basic concept. And so you’re probably asking how can we, on the line after the ByVal API call, use strWinDir in code like that? You see, really, it should still hold that set of 255 spaces we initially set strWinDir to.
But when you pass strings to an API call ‘ByVal’ – behind the scenes, Visual Basic actually passes a pointer to the original string. In other words, it passes your string ByRef meaning the API code can change the value of the original string.
I know, I know – don’t ask me why, it’s just another peculiarity but that’s how the API changes strWinDir and allows you to use it later in the code. Just thought I’d attempt an explanation, weird though it may be.
But if you’re not familiar with ByVal and ByRef whatnots, you can ignore the last few paragraphs.
<Reader: NOW you tell me!>
And now for that finally line of code:
Here, we’re just displaying the strWinDir variable after it’s been passed in and out of the API call, then trimmed for nulls.
Let’s now add that extra TrimNull function I was talking about:
- Add the following code to your module:
Public Function TrimNull(MyString As String) Dim intPosition As Integer intPosition = InStr(MyString, Chr$(0)) If intPosition Then TrimNull = Left(MyString, intPosition - 1) Else: TrimNull = MyString End If End Function
Once again, this simply checks the passed string for a terminator, Chr(0). If it finds one, it deletes it.
Please, take this function and treasure it. You’ll be able to use it in many of the API calls involving strings, making it a jolly useful method to have around.
But now, let’s get testing!!
- Run your application and hit the Command Button
What happens? Hopefully and I’m keeping my fingers crossed for you this end the message box should display your Windows path! Well done!
Now that may seem like a lot of work just to get a few characters of information, but you only have to write it once. You could, perhaps, throw the code behind a function, residing in a common module and call it from anywhere within your application.
And it’s a lot better than… erm… presuming. D’OH!
Next up, I’d like you to go ahead and attempt a few of your own API calls. Try using the below pair to get both the System and Temporary directory:
Public Declare Function GetSystemDirectory Lib "kernel32" _Alias "GetSystemDirectoryA" _(ByVal lpBuffer As String, ByVal nSize As Long) As LongPublic Declare Function GetTempPath Lib "kernel32" _Alias "GetTempPathA" _(ByVal nSize As Long, ByVal lpBuffer As String) As Long
Go on, follow all the rules we’ve discussed so far and have a go! Make sure you note how the second one declares it’s arguments back-to-front.
You might also want to try the GetComputerName call, also rather similar:
Declare Function GetComputerName Lib "kernel32" Alias "GetComputerNameA" _(ByVal lpBuffer As String, nSize As Long) As Long
Note: A download of the GetWindowsDirectory API call is available here.
Fancy a cunning quickie? Hey, who doesn’t…
- Create a new VB project
- Lookup and declare the following in the form:
Private Declare Function PaintDesktop _ Lib "user32" _ (ByVal hdc As Long) As Long
I decided to put this behind a form just to show you it can be done. However when you do declare API calls like this, they have to start with the ‘private’ keyword and can only be called by that one form.
- On your Form, add a Command Button
- Place the following code behind the button:
Here, our API call is asking for hdc. First off, just remember that any argument that starts with h is a handle, a pointer of some kind.
We’ve already encountered hWnd, which is the handle of a window even though a ‘window’ can be anything from a form to a control.
Now the dc bit of hdc stands for ‘device context’ and is, in effect, a pointer to the ‘graphical side of stuff’. So whereas hWnd could be a pointer to a form (or rather, a window), hdc is a pointer to the potentially graphical part of the form. Controls such as the PictureBox also have a hdc property.
And as we’re talking to user32.dll in this call that’s the user interface part of the Win32 API, remember? you could guess we’re doing something slightly visual.
So in this code, we’re simple passing the PaintDesktop function your form’s graphical pointer, hdc. And this function uses it to… to do what?
I’m not telling, you’re going to have to find out yourself but it’s very cool and usually only used by desktop replacement programs.
Note: A download of the PaintDesktop project is available here.
In this section, I’ll be sending you off on five unique tasks from around the Web.
You’ll be asked to disable Ctrl-Alt-Delete key combinations, display that lovely ‘Browse for Folders’ dialog box, force a Windows restart, get information on a drive, plus display the File Open dialog box without the bulky Common Dialog control.
So, five tasks to complete that will boost your API knowledge no end. Some of these teasers are simple, others more complicated. But each will offer something new and challenging, an awareness of some extra functionality or problem yet to be uncovered.
Please, ensure you complete these tasks before continuing to the end of this tutorial. All ready? Good luck!
- Disabling Ctrl-Alt-Delete – Sometimes you don’t want users trying to exit your application using that horrid Ctrl-Alt-Delete combination. So stop them!
- Browse for Folders – You’ve probably seen the ‘Browse for Folders’ dialog in applications such as Paint Shop Pro or Frontpage. Well, now you can have it too. Note how you use a special data type here, build it up then pass to the API call. Also, notice that you aren’t using one of the standard Win32 DLLs (User32, Kernel32, Gdi32) here.
- Restart Windows – Need to restart Windows after that installation routine? Make users log off, shut down, reboot and more — with this groovy API call.
- Get the Drive Type – Is it a bird, is it a plane? Nope, it’s a floppy disk drive. How do you know? Well, that’s where this groovy GetDriveType call steps in. Check it out!
- File Open Dialog – You can use the API to display a basic file open dialog box to replace the bulky Common Dialog control that bundles with VB. Note the OPENFILENAME type here, too — as with the Browse Folders section above. It also passes strings around, as we did earlier with the GetWindowsDirectory function.
Have you finished already? Wow. How did you find the tasks?
I can imagine the Browse for Folders and File Open Dialog activities were the most difficult. A new concept was introduced in both those samples types, or User Defined Types (UDTs) to most VB’ers. Some API folk also call them structures.
As an example of this, the File Open Dialog activity used a call titled ‘GetOpenFileName’. If you open the API Viewer, you should be able to find this under the Declares section. You’ll notice it asks for one parameter ‘pOpenfilename As OPENFILENAME’.
If you change the API Type combo to Types, you can look up the ‘OPENFILENAME’ Visual Basic UDT. And hey look, it’s exactly the same as the code in that article!
So you can see the thought process behind this. You now know where the author of that particular article got his information.
Of course, before running that function, the writer had to generally know what it did and how to use it. That may have been discovered via the Microsoft API documentation, another Visual Basic website, one of the variety of sources mentioned earlier — or, heck, it could’ve just been a darn good guess.
What have we learned from this section? Well, you’ve reinforced all the API skills you’ve picked up so far, checked out a few neat calls, plus uncovered the concept of ‘types’, which you may often find yourself passing to the API.
That’s the good news. And now…
Unfortunately in the aromatic garden of API calls, not everything smells of roses. You get the occasional pile of manure, dumped by some ignorant sheep farmer.
And in this section, we’re going to briefly cover a couple of the big stinks you may encounter plus give a little advice on how to avoid them.
First up on our naughty-naughty list, we have the API Viewer. Sure, it’s been nicer than pie with us so far. But that’s because we’re just starting out. Once it gets to know you, it’s start lying. Yep, lying.
For example, one of the best-known instances of the API Viewer telling a little fib is with the GetPrivateProfileString function, which allows developers to peek inside INI files and return values.
Unfortunately, earlier versions of the API Viewer declared this call incorrectly, causing much ‘developer distress’, a recognised medical condition. Thankfully this problem has been (debatably) fixed in the VB6 API Viewer, though a few lesser-used declarations are still flawed.
So if you’re sure that code is correct but the API still insists on being horribly disobedient be sure to check out either the VB API reference site (www.vbapi.com/ref/) or Microsoft’s API documentation (www.microsoft.com/api/) for assistance.
If you’ve not worked with arrays before, ignore this. Even if you have, you’ll probably never need to pass one to an API call. But just in case, a warning – don’t pass WholeArrays() direct to API calls. Instead, pass the first element of an array, RatherLikeThis(1). If you wanted the function to start work on the third element of the array onwards, you could pass it SomethingLikeThis(3). Either way, pass elements of an array rather than whole arrays. In addition, when doing this you usually have to pass the size of the arrays along with the API call.
You can’t pass the Currency data type to an API call. It’s a VB-only thang. Instead, convert it to a Long or Double first.
There are a few other issues you may notice cropping up here and there, too. Buzzwords include memory alignment, the LenB function, subclassing and AddressOf function pointers, but that jargon is just beyond the scope of this API tutorial.
But if you’re eager to learn more about all that jazz, read on…
Well you’ve already picked up a helluva lot from this tutorial. But if you’re wanting to take your API skills to the next level, read on.
First off, there’s always the eternally groovy API tutorial hosted at www.vbapi.com/articles/intro/index.html
It’s not so excellent for the beginner, but hey, that’s no longer yourself. You should be able to follow this through, taking you to the next level.
If you’re looking for examples to play around with, a general Web search will undoubtedly prove fruitful. You can grab a few general links at About.com here.
And if you have a few bucks spare, there’s always the traditional tech book route to follow. Here are two of the best-known API publications:
- Win32 API Tutorial #37/$40 – A well put together tutorial by a guy that’s been there, done that. Plenty of practical examples with a lightweight writing style. By Jason Bock. Click here for more information.
- Visual Basic Programmer’s Guide to the Win32 API #55/$60 – More a reference book of the top Win32 API functions. Expensive, yet a useful addition to any developer’s bookshelf. By Dan Appleman. Click here for more information.
Well, that’s it for this section time to get all conclusive…
The truth? You wanna know the truth? Hah, you can’t handle the truth.
Still, it’s going to be a very short conclusion if I don’t tell you so here goes: Visual Basic isn’t really Visual Basic. Oh no.
<Reader: So who is it, Karl? Who?>
You see Visual Basic is really just… <Karl steps over to VB and pulls off the wig. There is a mass gasp> … that’s right, a whole bunch of API statements in a very good disguise.
Oh yes. Although Visual Basic hides all the intricacies of its complex API calls under the hood, they’re always sitting there, waiting.
Every time you run – Form1.Print "Hello" Visual Basic tootles off and calls the TextOut API function, passing all the required parameters.
And when a user clicks your Command Button, Windows sees this. It then sends a message (via the very popular SendMessage function) to Visual Basic saying, "Hey, some geek just clicked your button". VB analyses the message, realises what’s happening then raises the Click event of your Command Button, thereby executing any code within it.
And phew! – the list goes on.
As you can see, Visual Basic hides a lot of stuff from you. In fact, Windows is all really just a whole bunch of API calls cobbled on top of each other.
And in this two-part series, we’ve taken a peek at some of the things our favourite programming language manages to cover up so very well.
More specifically, today we’ve looked at giving our application the Office look, discovered how to retrieve the Windows path, plus checked out how to display the desktop background on a form all via the API. We went on to fiddle with a few activity projects, plus found out where you can take your skills from here.
I hope this tutorial has helped you understand more about the API and if I’ve done my job right, you won’t feel too nervous about utilising its potential in future projects.
At times it might seem about as pointless as French marriage vows, but bear with it. You’re going under the hood, touching those parts regular code can’t reach. And that takes a bit of work but it can be very rewarding too.
People have used the API for allsorts. From simply enhancing their regular programs to building little add-ons that run on top of other applications, such as certain AOL utilities. Some even use the API in conjunction with ActiveX controls (see our tutorial here – www.vb-world.net/activex/controls/) to build lightweight, reusable components.
And now you have the knowledge to do this too. Whatever you get up to please let us know by posting feedback on the bulletin board. You can also mail me personally – [email protected]
But until the next time, this is Karl Moore signing off, concluding this API tutorial and wishing you all the very best in anything you may do. Goodnight!