Posts filed under 'Code Examples'

Extending My.Computer.Audio To Play MP3’s

Extending My.Computer.Audio To Play MP3’sThe My.Computer namespace in VB.NET 2005 provides several ’speed dials’ to various system functions. For example, it can be used to play WAV audio files. Unfortunately, it can’t play other media types like MP3, WMA or MIDI so that limits its usefulness. However, you can extend this, or any other My namespace class, by using partial classes. That’s what I’ll be demonstrating in this article.

For this demo, I used the “PlayingSounds” project from the 101 Visual Basic Samples for WinForms examples from MSDN. These little demo programs are great quick examples on how to do a number of things in VB although they often need a few loose ends tied up to make them work their best.

To get started, we open up the Application Events module. This module exposes the My namespace where we can add functionality to our applications. I covered using it in this earlier article: Tip Sheet: Application Events. Now we need to add a Partial Class to the My namespace to overload the Audio property, like so:

Partial Class MyComputer

Overloads ReadOnly Property Audio() As Audio
    Get
        Return New Audio
    End Get
End Property

End Class

This returns our own Audio object rather than the built in one. Now we are ready to create our own Audio class to play the additional sound files.

First, we need to load a reference to Windows Media Player. So, we go to My Project and the References tab and select “Add Reference”. WMP is a COM object so we select that tab and pick Windows Media Player (wmp.dll) from the list.

Next, we start our own Audio class by inheriting the base Audio class:

Public Class Audio
    Inherits Microsoft.VisualBasic.Devices.Audio

The next part of our code contains a shared reference to the Windows Media Player as well as a indicator of the type of sound we’re playing, if any. Since we only want to play one sound file at a time we use a shared variable here. Also an enum for the sound types is added. We want to keep track of which type of sound is loaded into our audio object since system sounds will use the original Audio class. Here’s the code:

Private Enum SoundTypes
    NoSoundLoaded
    WmpSound
    WavAudioSound
End Enum

Private Shared SoundType As SoundTypes = SoundTypes.NoSoundLoaded
Private Shared WMP As New WMPLib.WindowsMediaPlayer

Now we want to create our overloads for the Play function. The first one has no parameters:

Public Overloads Sub Play()
    If SoundType = SoundTypes.WmpSound AndAlso _
                WMP.playState = WMPLib.WMPPlayState.wmppsPaused Then
                    WMP.controls.play()
    End If
End Sub

In this routine we want to continue playing the currently loaded sound if we’re playing a sound using Windows Media Player and the player is paused. Otherwise, we want to ignore this command. Note that the AndAlso ’short circuits’ the If..Then so if the type is not WMP the state of the player isn’t checked.

Our next two Play overloads allows us to pass in a file location to play. Note that this can be either a local file location or even a URL on a web site. The first only accepts a file location and sends the actual processing to the second play routine which also accepts the Microsoft.VisualBasic.AudioPlayMode.

Public Overloads Sub Play(ByVal fileLocation As String)
    Play(fileLocation, AudioPlayMode.Background)
End Sub

Public Overloads Sub Play(ByVal fileLocation As String, _
                                   ByVal playmode As Microsoft.VisualBasic.AudioPlayMode)
    If SoundType <> SoundTypes.NoSoundLoaded Then
        Me.Stop()
    End If
    Select Case New FileInfo(fileLocation).Extension.ToLower
        Case ".wma", ".mp3", ".mid", ".wav"
            WMP.URL = fileLocation
            Select Case playmode
                Case AudioPlayMode.BackgroundLoop
                    WMP.settings.setMode("loop", True)
                    WMP.controls.play()
                Case Else
                    WMP.settings.setMode("loop", False)
                    WMP.controls.play()
            End Select
            SoundType = SoundTypes.WmpSound
        Case Else
            Throw New Exception("Invalid File Type")
    End Select
End Sub

We want to stop any sound that is currently playing first and then load the new sound into Windows Media Player and play it. If the file extension isn’t a recognized type, we throw an exception. Another approach would be to try to play anything passed to the player and capture any exceptions raised by the player object or to simply ignore any invalid file names without raising an exception. If the second parameter passed in to the routine is AudioPlayMode.BackgroundLoop then we want to play the file over and over until it is stopped. Otherwise, we only want to play it once.

Our remaining Play overloads call the base class. In addition to this call, we add code to stop the currently playing file and an indicator of the file type being played. For the system sounds overloads we add an overload to the standard Play method for this purpose as well as overloading the base PlaySystemSound method.

Public Overloads Sub Play(ByVal systemSound As System.Media.SystemSound)
    If SoundType <> SoundTypes.NoSoundLoaded Then
        Me.Stop()
    End If
    MyBase.PlaySystemSound(systemSound)
    SoundType = SoundTypes.WavAudioSound
End Sub
Public Overloads Sub PlaySystemSound(ByVal systemSound As System.Media.SystemSound)
    If SoundType <> SoundTypes.NoSoundLoaded Then
        Me.Stop()
    End If
    MyBase.PlaySystemSound(systemSound)
    SoundType = SoundTypes.WavAudioSound
End Sub
Public Overloads Sub Play(ByVal mediaData() As Byte, _
                                             ByVal playMode As Microsoft.VisualBasic.AudioPlayMode)
    If SoundType <> SoundTypes.NoSoundLoaded Then
        Me.Stop()
    End If
    MyBase.Play(mediaData, playMode)
    SoundType = SoundTypes.WavAudioSound
End Sub
Public Overloads Sub Play(ByVal mediaStream As System.IO.Stream, _
                                             ByVal playMode As Microsoft.VisualBasic.AudioPlayMode)
    If SoundType <> SoundTypes.NoSoundLoaded Then
        Me.Stop()
    End If
    MyBase.Play(mediaStream, playMode)
    SoundType = SoundTypes.WavAudioSound
End Sub

Now that we have all the play routines taken care of, we’re ready to stop the playing.

Public Overloads Sub [Stop]()
    Select Case SoundType
        Case SoundTypes.WmpSound
            WMP.controls.stop()
        Case SoundTypes.WavAudioSound
            MyBase.Stop()
        Case Else
            'do nothing - if desired, add code to throw an exception here
    End Select
    SoundType = SoundTypes.NoSoundLoaded
End Sub

In this routine we use our sound type enum to determine which method we should use to stop the sound. Note that the Stop routine name is in brackets so that the compiler won’t get it confused with the base VB command of the same name.

Lastly, we add a Pause method. This isn’t in the base class but we’re extending this base to add new functionality.

Public Sub Pause()
    Select Case SoundType
        Case SoundTypes.WmpSound
            WMP.controls.pause()
        Case Else
            'do nothing - if desired, add code to throw an exception here
    End Select
End Sub

Files being played in the base Audio class can only be stopped and started but by using WMP we can pause them as well. This adds another useful function to our new inherited class.

The rest of the application is simply a few improvements to the example program. I added in a pause button checkbox and made a few other enhancements to the program, such as adding in XML comments and improving the file lookup. The code should be fairly self-explanitory but feel free to ask any questions about it here in the comments section. Also, if you notice any bugs or have suggestions for enhancements, please leave a comment.

You can download the code for this project here: PlayingSoundsPlus Code Example

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

3 comments July 17th, 2007

MDI PDF File Viewer with Tabbed Header

Tabbed FolderThis code example attempts to answer two common questions. First, how can I load PDF files into my VB.NET application for viewing? And, second, how can I create a MDI application that has tabbed browsing like Visual Studio, MSDN, and IE7? While there are more complex, and perhaps more elegant, ways to accomplish these tasks, this example offers a simple, easy-to-use way to implement these features. Plus it shows how to deal with some quirks you might encounter.

First, let’s look at what we need to do to load PDF files. In our case, we’re going to assume that potential users have Internet Explorer and Adobe Acrobat Reader already installed on their systems and configured correctly. If you can’t safely make this assumption, then you’ll need to look into more complex methods to accomplish this task. But, in most organizations, this code should work fine.

Having these ready made components reduces our work to a few simple commands. First, we need to place the Web Browser component on a form. In the case of this example, this will be a MDI child form. We will dock it so that it fills the entire form. Next, what do we need to do to load the PDF? That’s simple too. All we need to do is use the Navigate method to load the PDF into the browser, like so:

wbDocument.Navigate(filename)

So, we can achieve this seemingly daunting task with a control and one command, pretty easy. However, unloading is where we have to use a little trick to avoid a potential problem.

The problem that can be encountered is that Acrobat, under some circumstances, will hang onto a reference to a PDF file, thus keeping it locked. This may not be a big deal in some cases since the lock will be released eventually or at least when the application ends. But in other cases, the locked file may prevent actions your program or users need to take. To insure that Acrobat releases the file, we have to clear it out of our web browser completely. We can use the following code to do this:

wbDocument.Hide()
wbDocument.Navigate("about:blank")
Do Until wbDocument.ReadyState = WebBrowserReadyState.Complete
    Application.DoEvents()
    System.Threading.Thread.Sleep(100)
Loop
wbDocument.Dispose()
System.Threading.Thread.Sleep(100)

Here we hide the browser control, navigate to the default blank page, wait for the browser to completely load this page, then dispose of the browser control and wait a brief moment before continuing. You may not need all this waiting/sleeping but I’ve found that having these short waits helped insure that the PDF file is completely freed. Try it with and without this code and see how it performs for you.

Now, let’s create a tabbed browser for our MDI app. This too is rather easy to do as well. First, we add a tab control to our MDI parent form and dock it to the top and size it appropriately. We don’t need to add any tabs to it at this point. Next, in our MDI parent’s code, we want to include loading to the tab with the loading of a child form, like so:

Private Sub LoadNewViewer(ByVal fileName As String)
    If My.Computer.FileSystem.GetFileInfo(fileName).Extension.ToUpper = ".PDF" Then
        For Each frm As Form In Me.MdiChildren
            If frm.Text = fileName Then
                frm.BringToFront()
                tabWindows.SelectedTab = tabWindows.TabPages(fileName)
                Exit Sub
            End If
        Next
        Dim ChildForm As New frmViewer
        ChildForm.MdiParent = Me
        ChildForm.Text = fileName
        ChildForm.wbDocument.Navigate(fileName)
        tabWindows.TabPages.Insert(0, fileName, fileName)
        tabWindows.SelectTab(0)
        For Each frm As Form In Me.MdiChildren
            frm.WindowState = FormWindowState.Normal
        Next
        ChildForm.Show()
        ChildForm.WindowState = FormWindowState.Maximized
    End If
End Sub

That may seem like a lot of code but it performs a number of functions that improve how the app performs and looks.

After making sure the file type is what we want to load, we first check to see if the document is already loaded into our application. If it is, we bring it to the front and select the corresponding tab. In our case, we’re making it simple by using the filename as the key and display text for the child form and the tab but you could add a more complex method if you needed to do so. If the form is already loaded, we’re done, but if it isn’t we need to create a new child form in the next steps of the application. Finally we load in the PDF document and show it and we’re done, or are we?

If you don’t mind showing the default MDI child icon, then yes, you’re done. But, if you want to show your own icon, there is a little trickery involved here. To get your icon to show you have to normalize each of the child forms before showing your new form. This does cause a bit of flicker so you have to decide if you want icons or a little flicker in your app. In addition to this step, you will also need to load the icons in the New method of the MDI parent and child forms. This is a little quirk in the MDI interface and this is the easiest way to work around it.

» Click here to download VB.NET source code for this article.

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

3 comments June 23rd, 2007

Base64 Encoding

Base64 Encoding works like magicOften a requirement is given to store binary information, such as a bitmap, inside an XML file or other text file. One way to do this is to encode the file information using Base64 encoding. This code example covers how to encode and decode file data using this method.

First of all, you may ask, “What is Base64 Encoding?” If you want to know all the nuts and bolts of it, check out this Wikipedia article — Base64 — but the easy answer is that it’s a method to encode binary data using only a-z, A-Z, and 0-9 characters.

In the example program, we will take a specified file, read it into memory and encode it as a Base64 string, place it in an XML file, then extract the encoded file from the XML file and write it to disk. First, let’s look at the encoding routine.

Public Function ConvertFileToBase64(ByVal fileName As String) As String
    Dim ReturnValue As String = ""
    If My.Computer.FileSystem.FileExists(fileName) Then
        Using BinaryFile As FileStream = New FileStream(fileName, FileMode.Open)
            Dim BinRead As BinaryReader = New BinaryReader(BinaryFile)
            Dim BinBytes As Byte() = BinRead.ReadBytes(CInt(BinaryFile.Length))
            ReturnValue = Convert.ToBase64String(BinBytes)
            BinaryFile.Close()
        End Using
    End If
    Return ReturnValue
End Function

In this routine we check that the specified file exists then open it as a stream. Then we read the data into a BinaryReader object and extract it to a byte array. Note that the number of bytes to read can be specified so that for larger files you could enhance this code to use chunking rather than reading everything in at once. The last step of the process is to convert the byte array to a Base64 string using the built-in Convert class. Also, the Using block insures that the file resource is disposed of properly.

Now we have a string of data we can place in a XML file, a database record, or anywhere else we could place a string. In this example, we will be placing the encoded data string into a XML file as an element.

OK, so now that we have the data in the XML file and saved it to disk, how do we get it back out again? The following code shows how this is done:

Public Function ExtractBase64ToFile(ByVal fileBase As String, ByVal extractFileName As String) As Boolean
    Dim FileBytes() As Byte
    FileBytes = Convert.FromBase64String(fileBase)
    My.Computer.FileSystem.WriteAllBytes(extractFileName, FileBytes, False)
End Function

To reverse the process, we load the string into a byte array and write it to disk. It couldn’t be much simplier. As with the encoding routine, this one could be further enhanced by adding chunking logic and additional exception management.

Using Base64 encoding within VB.NET is an easy way to embed binary data in your portable data files. This applies not only in XML files but other EDI file types, such as HL7 files used in the healthcare field. You can also use it with your local data files to obscure information from the casual viewer. There are many uses for this technique and it’s a useful addition to your VB.NET toolkit.

» Click here to download VB.NET source code for this article.

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

2 comments June 3rd, 2007

System.Net.Mail How To

You've Got System.Net.Mail!VS 2005 / Framework 2.0 adds a new way to email, the System.Net.Mail Namespace that uses SMTP (Simple Mail Transfer Protocol) to deliver messages. In this article, I’ll provide a simple example of how to use it and go over a couple of quirks that people get hung up on.

The MailMessage and SmtpClient classes are the heart of this new Namespace and sending an email is really just a matter of a couple of lines.
.
.
.
.
.

Dim EmailServer As New System.Net.Mail.SmtpClient(EmailServerName)
EmailServer.SendAsync(TestEmail, Me)

The code above makes a couple of assumptions. First, that the variables for the call have been filled with correct information and, second, that the target SMTP server uses Windows authentication. Also note that the MailMessage class implements the IDisposible interface so you might think it’s a candidate for the Using block as I mentioned in a previous article. However, since it is called async, the Using block would dispose of it prematurely so this is an exception to the rule where you should not use Using.

In addition to the basics of From, To, Subject, Body, you can use properties of the MailMessage to add attachments, set the CC and BCC, select the encoding method for the body, and do a few other things. For example, if you wanted to add a CC to the message above the code would look like this:

Dim TestEmail As New System.Net.Mail.MailMessage(EmailFrom, EmailTo, EmailSubject, EmailBody)
TestEmail.CC = EmailCC
Dim EmailServer As New System.Net.Mail.SmtpClient(EmailServerName)
EmailServer.SendAsync(TestEmail, Me)

If you wanted to add an attachment to the email, the code would look like this:

Dim TestEmail As New System.Net.Mail.MailMessage(EmailFrom, EmailTo, EmailSubject, EmailBody)
TestEmail.CC = EmailCC
TestEmail.Attachments.Add(New System.Net.Mail.Attachment(EmailAttachmentFileName))
Dim EmailServer As New System.Net.Mail.SmtpClient(EmailServerName)
EmailServer.SendAsync(TestEmail, Me)

There are several overloads available for creating attachments so you may want to consult MSDN help if you need more advanced options.

In the demo program, I keep it simple without CC’s or attachments although I do add in a callback for the async operation. Using the callback allows you to verify that the email was sent or not sent without tying up your program waiting for a response.

What if you need to authenticate onto a remote SMTP server where you don’t have Windows authentication available? In this case, you’ll need to create credentials, like so:

Dim TestEmail As New System.Net.Mail.MailMessage(EmailFrom, EmailTo, EmailSubject, EmailBody)
TestEmail.CC = EmailCC
TestEmail.Attachments.Add(New System.Net.Mail.Attachment(EmailAttachmentFileName))
Dim EmailServer As New System.Net.Mail.SmtpClient(EmailServerName)
EmailServer.Credentials = New System.Net.NetworkCredential(“emailaccount@remoteserver.com”, “mypassword”, “mail.remoteserver.com”)
EmailServer.SendAsync(TestEmail, Me)

In most cases you will need to provide the domain name on the email account ID and you’ll need to confirm that the mail subdomain is correct and allows this kind of login. Your password is sent as clear text basic authentication in this scenario so be careful! You can use other options to provide a more secure connection if your SMTP server supports them.

Now for a couple of quirks with System.Net.Mail.

First, if you’ve used other and earlier mail engines you may have gotten used to separating multiple TO email addresses with a semi-colon, like so:

EmailTo = “mrblah@mydomain.com; msblank@mydomain.com”

However, in this case a formatting exception will be thrown. Yes, it surprised me too the first time this happened. The correct delimiter to use with System.Net.Mail is a comma, like so:

EmailTo = “mrblah@mydomain.com, msblank@mydomain.com”

My other quirk that you might encounter is that the low level code that System.Net.Mail uses might get mistaken for a mass emailing virus by some versions of anti-virus software. While these anti-virus block System.Net.Mail email they will allow email by older CDO and System.Web.Mail methods to function normally. If you encounter this problem, the solution is the consult with the anti-virus company to see if they have an update available or you might want to risk disabling this part of the anti-virus protection, depending on your circumstances. As a last resort, you can fall back to the older methods that should work OK.

» Click here to download VB.NET source code for this article.

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

7 comments May 27th, 2007

Using Binary Serialization

Scotty, beam up my objects.Binary Serialization is the process of storing the state of an object in a binary stream. The public and private members of the object as well as the name of the class are converted to a stream of bytes. This data can be written to disk, transmitted, or other managed in other ways that are applicable to data streams. When the object is deserialized, an exact clone of the original object is created. Think of it as a Star Trek style transporter for your objects.

This simple example shows how to save and restore the contents of a listbox using Binary Serialization. In the application, the program reads the listbox data from disk, if it exists, then the user enters new items into the listbox. They may also clear one, many, or all of the items from the list and save the data if they wish. Finally, when the program is closed the user has the option to save the data to disk again. Let’s look at some of the details.

First, we have to setup our references right to use Binary Serialization. We will need use the following:

Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary

They should be loaded as the default references but you will need to import them or type quite a long line of code to reference them.

Next, in the Form’s load routine we see how to use the BinaryFormatter:

Using fs As New IO.FileStream(_dataFile, IO.FileMode.Open, IO.FileAccess.Read)
    lstItems.Items.AddRange(DirectCast(New BinaryFormatter().Deserialize(fs), Object()))
End Using

Note that we put the FileStream in a Using block to insure that it releases the file after it reads in the data. See my article on Using for more details on this. In the block we use the AddRange method of the Listbox’s Items collection to load the deserialized Object array contents into the listbox.

Why did we need to use an Object array rather than the Listbox’s built-in ObjectCollection? The reason for this is that the ObjectCollection is not serializable. In order for an object to be serialized it needs to be built that way and the ObjectCollection nor the Listbox itself are serializable. So, we will need to convert the items to and from Object arrays in this case. In your own classes you want to serialize, use the Serializable attribute to mark it this way, as in this example:

<Serializable()> _
Public Class MyClass

Also, we use DirectCast insure that .NET knows exactly what kind of object we’re expecting to be loaded.

Now, when we’re prepared to save the listbox data, we reverse the process. Once again we use the Using block for FileStream operations.

Using fs As New FileStream(_dataFile, IO.FileMode.Create)
     Dim items(lstItems.Items.Count - 1) As Object
     lstItems.Items.CopyTo(items, 0)
     Dim bf As New BinaryFormatter
     bf.Serialize(fs, items)
     fs.Close()
End Using

Next, we will need to copy the ListBox’s items from its ObjectCollection to an object array in order to serialize it. We do this by using the CopyTo method. Next the array is serialized to the FileStream and we’re done.

Binary serialization is a effective tool to use when you need to persist data without a using a database or when you want to transfer data from one location to another. Web Services often use it or its cousins XML and SOAP serialization to transfer data. It’s a great tool to add to your VB.NET bag of tricks.


» Click here to download VB.NET source code for this article.

Share This Article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Reddit
  • StumbleUpon
  • Technorati
  • DotNetKicks
  • DZone

Add comment May 25th, 2007

Next Posts Previous Posts


Most Popular Articles

Highest Rated Articles

Categories

Most Recent Articles

Feeds

 Subscribe in a reader

To subscribe by e-mail
Enter your address here

Delivered by FeedBurner

VB Opportunities

Archives