AllAPI Network - The KPD-Team

 
Allapi Network
 API-Guide
 ApiViewer

 API List

 
API Resources
 Tips & Tricks
 VB Tutorials
 Error Lookup
 
Misc Stuff
 VB examples
 VB Tools
 VB Links
 Top Downloads
 
This Site
 Search Engine
 Contact Form
 

Donate to AllAPI.net

Using DLLs and The Windows API

Windows' Handles

Visual Basic provides you with a nice soft buffer between your code and the underlying Windows DLL calls. One of the areas where this is most evident is in a control's properties.

Take the form as an example. Windows uses something called a structure to hold information about a form. This information is almost identical to the information contained in the form's properties window. However, whereas you or I can easily check a window's properties-we just click on a form and press F4 to bring up its Properties window-Windows stores each window's structure in a large list of data structures which relates to every window of every program actually running. To determine which structure relates to which window, it uses something called a handle. It can't use the name of the form to find it, because the name is just a property of that form. The handle is Windows' own shorthand ID for an object.

As you start to use API calls more and more, particularly those that deal directly with your Visual Basic forms, you'll find handles cropping up again and again. Conveniently, Visual Basic stores handles as read only properties, which you can use to pass to Windows functions when required.

The property is called hWnd (handle to a window) and can only be accessed at run time. The property means nothing to your Visual Basic code, but it can be read, and passed as a parameter, to those API calls that need it. You'll find that almost any API call that could do something to a displayed window will need you to pass it an hWnd parameter so that the function knows exactly which window you want to deal with.

Declaring Parameter Types

When you declare the type of parameter that a DLL subroutine or function needs, it's important to make sure that the ByVal keyword is used whenever necessary.

With regular Visual Basic code, if you pass a parameter to a function by value, it tells Visual Basic that the function can only deal with a copy of the parameter you pass it. This is how you do it with a regular function:

  Function Square(ByVal Number As Double) As Double

The alternative to passing by value is to pass the variables by reference. Here, you effectively send the whole variable to the routine, not just a copy of its contents. Therefore, if the routine changes the parameter, those changes are also reflected in the original variable. If you don't specify ByVal, the variable will be passed by reference automatically. Have another look at Chapter 8 for a reminder of these keywords.

If you write an internal Visual Basic function or subroutines and you miss out the ByVal keyword, as long as you pass the correct number of parameters to the code, nothing serious will go wrong. Sure, you may get cases where variables passed as parameters are changed, causing your program to have weird results, but nothing really serious will happen. Windows won't crash, for example!

However, with DLLs, the situation is a little more serious. If you omit the ByVal keyword, Visual Basic actually passes a pointer to the variable. This number tells the function being called where the variable is stored in memory. It's then up to the function to go to that memory location and retrieve the value itself. This is also what happens in Visual Basic-only situations-but since, in that case, the functions and subroutines are all written in Visual Basic code, Visual Basic can cope. DLL code, written in a language like C, expects things to happen in a certain way, and can get quite upset when they don't.

If a DLL function expects a number, let's say between 0 and 3, and you pass it a variable by reference, the actual value passed could be something like 1,002,342, which would be the address in memory where your variable lives. The DLL function would then try to deal with the number 1,002,342 instead of a number between 0 and 3, the net result of which would be that your system crashes.

There are no nasty error messages here; you know when a DLL call is wrong because your system generally misbehaves, or locks up completely! One of the golden rules of messing about with API calls is save your work! Since you're venturing outside the protected world of Visual Basic, when things go wrong, it can easily result in the whole system crashing, and you losing your work. Always save your project before running code with API calls in it. The best way to do this is to check the Save Before Run box in the Options | Environment menu.

Using Classes with the API

It goes without saying that the API is a powerful weapon in the VB programmer's arsenal. However, if you were to put API calls throughout your VB apps, you would pretty soon end up with a garbled mass of code that makes no sense to anyone, except perhaps you. I can't count the number of times I have frantically hunted through a huge VB app in search of a certain function only to realize after much angst that the programmer is using an API call.

The solution with VB5 is to turn the Windows API into easily reusable classes (or ActiveX controls-but more on them later). Each API call can be categorized according to which part of Windows it deals with. These categories can, in turn, be translated quite effectively into VB classes.

For our examples, how about a multimedia class that encapsulates the functionality of the multimedia API calls and, thus, the entire Windows multimedia system? That's what we'll look at next.

We'll start off by seeing how the class that I've put together works, then delve inside and see what's really happening.

Using the Multimedia Class

I have a great reuseable class module here which neatly wraps up a lot of the functionality of the multimedia API in Windows. Use it in your apps to provide access to sound and video files without the overhead of the heavyweight multimedia control that comes with Visual Basic. Let's do some typing.

Start a new Standard EXE project in Visual Basic, and then go to the Project menu and add a brand new class into your application. Then, bring up the code window and type this little lot in...it looks huge but it really isn'tI've padded the whole thing out with comments to make it a lot easier for you all to see what's going on.

 

Option Explicit

'-----------------------------------------------------

' Name : MMedia.cls

' Author : Peter Wright, For BG2VB4 & BG2VB5

'

' Notes : A multimedia class, which when turned

' : into an object lets you load and play

' : multimedia files, such as sound and

' : video.

'-----------------------------------------------------

' -=-=-=- PROPERTIES -=-=-=-

' Filename Determines the name of the current file

' Length The length of the file (Read Only)

' Position The current position through the file

' Status The current status of the object (Read Only)

' Wait True/False...tells VB to wait until play done

' -=-=-=- METHODS -=-=-=-=-

' mmOpen <Filename> Opens the requested filename

' mmClose Closes the current file

' mmPause Pauses playback of the current file

' mmStop Stops playback ready for closedown

' mmSeek <Position> Seeks to a position in the file

' mmPlay Plays the open file

'-------------------------------------------------------------

' NOTES

' -----

'

' Open a file, then play it. Pause it in response to a request

' from the user. Stop if you intend to seek to the start and

' play again. Close when you no longer want to play the file

'--------------------------------------------------------------

Private sAlias As String ' Used internally to give an alias name to

' the multimedia resource

Private sFilename As String ' Holds the filename internally

Private nLength As Single ' Holds the length of the filename

' internally

Private nPosition As Single ' Holds the current position internally

Private sStatus As String ' Holds the current status as a string

Private bWait As Boolean ' Determines if VB should wait until play

' is complete before returning.

'------------ API DECLARATIONS -------------

'note that this is all one code line:

Private Declare Function mciSendString Lib "winmm.dll" _

Alias "mciSendStringA" (ByVal lpstrCommand As String, _

ByVal lpstrReturnString As String, ByVal uReturnLength As Long, _

ByVal hwndCallback As Long) As Long

Public Sub mmOpen(ByVal sTheFile As String)

' Declare a variable to hold the value returned by mciSendString

Dim nReturn As Long

' Declare a string variable to hold the file type

Dim sType As String

' Opens the specified multimedia file, and closes any

' other that may be open

If sAlias <> "" Then

mmClose

End If

' Determine the type of file from the file extension

Select Case UCase$(Right$(sTheFile, 3))

Case "WAV"

sType = "Waveaudio"

Case "AVI"

sType = "AviVideo"

Case "MID"

sType = "Sequencer"

Case Else

' If the file extension is not known then exit the subroutine

Exit Sub

End Select

sAlias = Right$(sTheFile, 3) & Minute(Now)

' At this point there is no file open, and we have determined the

' file type. Now would be a good time to open the new file.

' Note: if the name contains a space we have to enclose it in quotes

If InStr(sTheFile, " ") Then sTheFile = Chr(34) & sTheFile & Chr(34)

nReturn = mciSendString("Open " & sTheFile & " ALIAS " & sAlias _

& " TYPE " & sType & " wait", "", 0, 0)

End Sub

Public Sub mmClose()

' Closes the currently opened multimedia file

' Declare a variable to hold the return value from the mciSendString

' command

Dim nReturn As Long

' If there is no file currently open then exit the subroutine

If sAlias = "" Then Exit Sub

nReturn = mciSendString("Close " & sAlias, "", 0, 0)

sAlias = ""

sFilename = ""

End Sub

Public Sub mmPause()

' Pause playback of the file

' Declare a variable to hold the return value from the mciSendString

' command

Dim nReturn As Long

' If there is no file currently open then exit the subroutine

If sAlias = "" Then Exit Sub

nReturn = mciSendString("Pause " & sAlias, "", 0, 0)

End Sub

Public Sub mmPlay()

' Plays the currently open file, from the current position

' Declare a variable to hold the return value from the mciSendString

' command

Dim nReturn As Long

' If there is no file currently open, then exit the routine

If sAlias = "" Then Exit Sub

' Now play the file

If bWait Then

nReturn = mciSendString("Play " & sAlias & " wait", "", 0, 0)

Else

nReturn = mciSendString("Play " & sAlias, "", 0, 0)

End If

End Sub

Public Sub mmStop()

' Stop using a file totally, be it playing or whatever

' Declare a variable to hold the return value from mciSendString

Dim nReturn As Long

' If there is no file currently open then exit the subroutine

If sAlias = "" Then Exit Sub

nReturn = mciSendString("Stop " & sAlias, "", 0, 0)

End Sub

Public Sub mmSeek(ByVal nPosition As Single)

' Seeks to a specific position within the file

' Declare a variable to hold the return value from the mciSendString

' function

Dim nReturn As Long

nReturn = mciSendString("Seek " & sAlias & " to " & nPosition, "", 0, 0)

End Sub

Property Get Filename() As String

' Routine to return a value when the programmer asks the

' object for the value of its Filename property

Filename = sFilename

End Property

Property Let Filename(ByVal sTheFile As String)

' Routine to set the value of the filename property, should the programmer

' wish to do so. This implies that the programmer actually wants to open

' a file as well so control is passed to the mmOpen routine

mmOpen sTheFile

End Property

Property Get Wait() As Boolean

' Routine to return the value of the object's wait property.

Wait = bWait

End Property

Property Let Wait(bWaitValue As Boolean)

' Routine to set the value of the object's wait property

bWait = bWaitValue

End Property

Property Get Length() As Single

' Routine to return the length of the currently opened multimedia file

' Declare a variable to hold the return value from the mciSendString

Dim nReturn As Long, nLength As Integer

' Declare a string to hold the returned length from the mci Status call

Dim sLength As String * 255

' If there is no file open then return 0

If sAlias = "" Then

Length = 0

Exit Property

End If

nReturn = mciSendString("Status " & sAlias & " length", sLength, 255, 0)

nLength = InStr(sLength, Chr$(0))

Length = Val(Left$(sLength, nLength - 1))

End Property

Property Let Position(ByVal nPosition As Single)

' Sets the Position property effectively by seeking

mmSeek nPosition

End Property

Property Get Position() As Single

' Returns the current position in the file

' Declare a variable to hold the return value from mciSendString

Dim nReturn As Integer, nLength As Integer

' Declare a variable to hold the position returned

' by the mci Status position command

Dim sPosition As String * 255

' If there is no file currently opened then exit the subroutine

If sAlias = "" Then Exit Property

' Get the position and return

nReturn = mciSendString("Status " & sAlias & " position", sPosition, 255, 0)

nLength = InStr(sPosition, Chr$(0))

Position = Val(Left$(sPosition, nLength - 1))

End Property

Property Get Status() As String

' Returns the playback/record status of the current file

' Declare a variable to hold the return value from mciSendString

Dim nReturn As Integer, nLength As Integer

' Declare a variable to hold the return string from mciSendString

Dim sStatus As String * 255

' If there is no file currently opened, then exit the subroutine

If sAlias = "" Then Exit Property

nReturn = mciSendString("Status " & sAlias & " mode", sStatus, 255, 0)

nLength = InStr(sStatus, Chr$(0))

Status = Left$(sStatus, nLength - 1)

End Property

 

When you're done typing this in, go to the properties window and change the name of the class to MMedia. Then save it to your hard disk and call it MMedia.cls. Save the main project's form as TestMM.frm, and the project itself as TestMM.vbp. We'll be using this project later-and you never know, you may want to use the class again later in your own projects.

This class turns a set of common multimedia calls into a stand-alone class. When an object is created from this class, it functions in exactly the same way as a control-in that it has properties you can set and examine, along with methods that actually make the object do something. This fits in well with the way we think about controls, and makes using the API calls invisible.

The methods that the class supports are as follows:

 

Method

Description

mmOpen

Opens a file (video, sound, music etc.) ready for playback.

mmClose

Closes the open file down, preventing any more playback.

mmPause

Pauses playback of the current file.

mmStop

Stops playback permanently.

mmSeek

Seeks a specific position within the file.

mmPlay

Take a guess; plays the open file, more often than not causing your speakers to burst into life.

These methods are all individual routines in MMedia.cls and all make use of the multimedia API calls in some way. We'll take a more detailed look at some of them in a moment to give you a feel for how the code actually fits together.

The following properties are implemented as property procedures in the source file:

 

Properties

Description

Filename

The name of the currently open file.

Length

The length of the currently open file.

Position

The current position through the file-you can use this in conjunction with the Length property to give the user some visual feedback as to the status of playback.

Status

A text word indicating the status of the file (playing, paused, stopped etc.).

Wait

Set this to true to make your code stop until playback has completed, or false to multitask.

Before we take a look at how the class does its thing, let's take a look at how to use the class itself. Along the way, you'll also see how seamless incorporating API calls into an app can be if you wrap the calls up nicely in a VB class.

Try It Out - Using the Multimedia Class

1 Open the TestMM.vbp project we just created if it isn't already open.

2 Resize the main form and draw a command button and common dialog control on it so that it looks like this:

3 We want to pop up the common dialog when the command button is pressed, so that the user is able to select a file name. Bring up the code window for the command button's Click event, and type this little lot in:

Private Sub Command1_Click()

With CommonDialog1

.Filter = "WaveAudio (*.wav)|*.wav|Midi (*.mid)|*.mid|Video files (*.avi)|*.avi"

.FilterIndex = 0

.ShowOpen

End With

End Sub

4 If you run the program now and click on the command button, you'll see the familiar file open dialog appear, asking you to select a multimedia file.

5 Quick and painless so far-all that remains is to bring the multimedia class into being as an object, and actually make use of it. Cancel the dialog and drop back into design mode, so that you can enter just a little more code. Bring up the command button Click event again and change it so that it looks like this:

Private Sub Command1_Click()

Dim Multimedia As New MMedia

With CommonDialog1

.Filter = "WaveAudio (*.wav)|*.wav|Midi (*.mid)|*.mid|Video files (*.avi)|*.avi"

.FilterIndex = 0

.ShowOpen

End With

If CommonDialog1.Filename <> "" Then

Multimedia.mmOpen CommonDialog1.Filename

Multimedia.mmPlay

End If

End Sub

6 Run the program. Find a multimedia file on your hard disk (there should be a few in Windows\Media) and have a play.

7 Save this project, because we'll be using it again in a while.

Of course, you'll need to have a sound card installed to play WAV and MID files.

How It Works

In the first line of the command button Click event code, we create a multimedia object which is derived from the MMedia class. This turns a class (which at this point is something ethereal and theoretical) into an object (something that can be used).

For the more technically minded amongst you, this process is normally referred to as instantiation.

Private Sub Command1_Click()

Dim Multimedia As New MMedia

The four lines of code we added at the bottom make use of our new multimedia object by opening the selected file using the class' mmOpen method, and playing it using the class' mmPlay method.

If CommonDialog1.Filename <> "" Then

Multimedia.mmOpen CommonDialog1.Filename

Multimedia.mmPlay

End If

As you can hopefully see from this, wrapping API calls up in a nice class make life a whole lot easier. If this class were used in a commercial organization, then the programmers using it wouldn't have to know anything about the underlying API calls-they would only need to be trained in how to use the multimedia class.

And of course, it has one other major attraction. Once you've been through the ritual of regular system reboots, as you find and eliminate the bugs in your API calling code, you don't want to have to do it all again in another project. Wrapping the API part in a class provides a safe, tested, and 'plug-in' capability-reducing the number of the API's customary three-fingered salutes that you'll need!

Page [<< PREV] 1 2 3 4 [NEXT >>]
Back to Tutorials - Main

 

 


Copyright © 1998-2007, The Mentalis.org Team - Privacy statement
Did you find a bug on this page? Tell us!
This site is located at http://allapi.mentalis.org/