|
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 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 '----------------------------------------------------- ' 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:
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:
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
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 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!
|
Quick searches: Site Search | Advanced Site Search |
|
By using this site you agree to its terms and conditions VB Explorer and VBExplorer.com are trademarks of Exhedra Solutions, Inc. |