Code Focused

Calling Win32 API Functions in Visual Basic 2010

Sometimes your best option is to bypass the .NET Framework and make function calls to the native Win32 API.

Welcome to my first article for Visual Studio Magazine as author of On VB. For years the print version of On VB has explored issues related to the Visual Basic programming language, and that coverage is now being extended online. Each month I'll publish two On VB columns at VisualStudioMagazine.com. You'll also soon find me contributing to the On VB column in the pages of Visual Studio Magazine as well.

I have an extensive background in VB that extends back to VB version 1. I am a Microsoft MVP and senior developer and software architect for a Michigan consulting firm, with deep experience in both the VB and C# programming languages. The multi-lingual background affords me a unique perspective in assessing the strengths, weaknesses and opportunities presented by Microsoft's oldest programming language. I am also heavily engaged in database development and key Microsoft platforms like SQL Server, Language Integrated Query (LINQ) and Entity Framework.

Starting this month, I will explore the wonders of Visual Studio and the world of managed code, specifically as it is surfaced through the Visual Basic language. But first, I felt it appropriate to start off with a discussion of the usefulness of calling unmanaged code.

Getting Local Disk Info
During the heyday of Visual Basic 6, I wrote a complex commercial application and wanted to minimize support costs that arise from a rather finicky Visual Basic 6 runtime. The solution? Write custom methods with calls to the Win32 API for any functions normally provided by the VB6 runtime and eliminate it entirely from the application. Runtime compatibility can't be a real problem if the runtime is not there! Thousands of installations later, I am happy to report that the strategy was a success and proved out the value of knowing how to leverage the Win32 API.

The .NET Framework has an amazing collection of capabilities, yet there are still some functions available in the Windows API that lack a direct, managed code equivalent. For instance, managed code won't let you obtain certain information regarding local hard drives. If you want to determine the free space of a disk drive, you are best off using the GetDiskFreeSpaceEx function. And if you are interested in the drive information in terms of clusters, use the GetDiskFreeSpace function as shown below. To get similar information using .NET calls, you would use the My.Computer.FileSystem.GetDriveInfo statement, although cluster information is not available from it.

       Call Win32API.GetDiskFreeSpace(RootPath, SectorsInCluster, BytesInSector, 
NumberFreeClusters, TotalNumberClusters)

MessageBox.Show(String.Format("GetDiskSpace: Free Cluster Count in C: is {0}",
NumberFreeClusters))

See the complete code in Listing 1.

The Win32 API is useful for both desktop and ASP.NET applications. Variations on Listing 1 were successfully tested in Windows Forms, Windows Presentation Foundation, Console and ASP.NET applications. Note that for ASP.NET, the Win32 APIs operate against the server, not the client machine and that Win32 API calls are not permitted from Silverlight 3. Silverlight 4 faces a similar limitation, as attempting to access a Win32 API when running out of browser produces this error: "Attempt by security transparent method 'Button1Click' to call native code through method 'Win32APIMethod' failed. Methods must be security critical or security safe-critical to call native code."

Button Swaps, Version Checks and More
Another capability made available through the Win32 API is swapping the left and right mouse buttons using the SwapMouseButton function. The name is a bit misleading, since it does not swap the mouse buttons each time it is called. Rather, it ensures that the mouse buttons are reversed when called with a true (non-zero) value. To return the mouse buttons to their standard operation, call the same SwapMouseButton function with a false (zero) value.

Dim ReversedButtons As Integer = 1
Dim StandardButtons As Integer = 0

SwapResult = Win32API.SwapMouseButton(ReversedButtons)

SwapResult = Win32API.SwapMouseButton(StandardButtons)

See the complete code in Listing 2.

It can be very useful to know the exact version of Windows running your application. For this, use the Win32 API function GetVersionExA in kernal32.dll to obtain the MajorVersion, MinorVersion, BuildNumber and PlatformId.

Public Class WindowsVersion
Public Structure OSVersionInfo
Public OSVersionInfoSize As Integer
Public majorVersion As Integer
Public minorVersion As Integer
Public buildNumber As Integer
Public platformId As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128)> _
Public versionString As String
End Structure

Declare Ansi Function GetVersionEx Lib "kernel32.dll" _
Alias "GetVersionExA" (ByRef osvi As OSVersionInfo) As Boolean

Dim osvi As New OSVersionInfo
osvi.OSVersionInfoSize = Marshal.SizeOf(osvi)

If GetVersionEx(osvi) Then
Dim result As String =
String.Format("Windows Version: {0}.{1}.{2}.{3}",
osvi.majorVersion, osvi.minorVersion, osvi.buildNumber, osvi.platformId)
MessageBox.Show(result)
End If

See the complete code in Listing 3.

Another useful Win32 resource is the ShellExecute method, which opens a file for print in its associated application. ShellExecute allows for more fine control than the Visual Basic Shell statement. Below is an example of loading a text file into its default application.

    Public Declare Function ShellExecute Lib "shell32" _
Alias "ShellExecuteA" (ByRef hwnd As Integer, ByVal Operation As String,
ByVal Filename As String, ByVal Parameters As String,
ByVal Directory As String, ByVal ShowCommand As Integer) _
As Integer

Private Declare Function GetDesktopWindow Lib "user32" () As Integer

Dim ParentHandle As Integer = GetDesktopWindow()
Dim ShowMode As Integer = 1 'Normal
Dim result As Integer = ShellExecute(ParentHandle, "Open", "Sample.txt", "", ".", ShowMode)

See the complete code in Listing 4.

Power Down
Finally, we look at the ability to place a computer into hibernation through the Win32 API. The IsPwrHibernateAllowed function of Powrprof.dll returns a true (non-zero) value if hibernation is permitted, false (zero) if not. If permitted, the SetSuspendState function may be called to effect the hibernation. See Listing 5.

    Public Declare Function IsPwrHibernateAllowed Lib "Powrprof.dll" _
Alias "IsPwrHibernateAllowed" () As Integer

Public Declare Function SetSuspendState Lib "Powrprof.dll" _
Alias "SetSuspendState" (ByVal Hibernate As Integer,
ByVal ForceCritical As Integer,
ByVal DisableWakeEvent As Integer) As Integer

If (Win32API.IsPwrHibernateAllowed() <> 0) Then
Win32API.SetSuspendState(1, 0, 0)
End If

See the complete code in Listing 5.

The Win32 API is available to your application without adding any external references. Most of the capabilities of the Win32 API can be accessed through the .NET Framework but there is still unique or extended functionality available only through the API. You can explore this further through the MSDN article Microsoft Win32 to Microsoft .NET Framework API Map.

Note: All code listings are specific to Visual Basic 2010 due to lack of line continuation characters

About the Author

Joe Kunk is a Microsoft MVP in Visual Basic, three-time president of the Greater Lansing User Group for .NET, and developer for Dart Container Corporation of Mason, Michigan. He's been developing software for over 30 years and has worked in the education, government, financial and manufacturing industries. Kunk's co-authored the book "Professional DevExpress ASP.NET Controls" (Wrox Programmer to Programmer, 2009). He can be reached via email at [email protected].

comments powered by Disqus

Featured

Subscribe on YouTube