Interpreting Error and Result Values

Sometimes your best efforts can't keep the user from making a mistake or prevent the system from befouling perfectly good code. In those situations, you need to trap and report the error so the user has some idea of what's going on with the application. That's why you need to include some type of error reporting in your application. The actual error reporting process is relatively easy—not as easy as within .NET, but certainly easier than some developers think. Listing 3.4 shows the code you'll need to make this example work.

Listing 3.4: Reporting Win32 API Errors Is Relatively Easy Using This Code.

// Declare the LoadLibraryEx() function. [DllImport("Kernel3 2.DLL")]

public static extern IntPtr LoadLibraryEx(String lpFileName,

IntPtr hFile, Int32 dwFlags);

// Tell Windows to load the DLL as a data file.

public const Int32 LOAD_LIBRARY_AS_DATAFILE = 0x00000002;

// Declare the GetLastError() function. [DllImport("Kernel3 2.DLL")]

public static extern Int32 GetLastError();

// Declare the FormatMessage() function. [DllImport("Kernel3 2.DLL")]

public static extern Int32 FormatMessage(Int32 dwFlags,

IntPtr lpSource, Int32 dwMessageID, Int3 2 dwLanguageID, out String lpBuffer, Int32 nSize, Int32 Arguments);

// Constants used to format the message.

public const Int32 FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100 public const Int32 FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200 public const Int32 FORMAT_MESSAGE_FROM_STRING = 0x00000400

public const Int32 FORMAT_MESSAGE_FROM_HMODULE = 0x00000800 public const Int32 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000

public const Int32 FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000 public const Int32 FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF

private void btnTest_Click(object sender, System.EventArgs e) {

IntPtr hLib; // Handle of the library we want to load. Int32 ErrNum; // Error number. String ErrStr; // Error message.

// Attempt to load a non-existent library. hLib = LoadLibraryExCNothing.DLL",

IntPtr.Zero,

LOAD_LIBRARY_AS_DATAFILE);

// Determine there is an error.

// Retrieve the error. ErrNum = GetLastError();

// Change it into a string.

FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, IntPtr.Zero, ErrNum, 0, out ErrStr, 0, 0);

// Display the message on screen.

MessageBox.Show("Error Number: " + ErrNum.ToString() + "\r\n" + ErrStr, "Library Load Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

I've included the LoadLibrary() function in this example because it comes in handy for a number of purposes. For example, you'll use the LoadLibrary() function to read icons embedded in existing DLLs such as Shell32.DLL. However, in this case, we need to create an error condition, so I've asked LoadLibrary() to read a non-existent DLL file. This code will produce a simple error message.

You have to read the Win32 API documentation carefully when checking for error conditions. In this case, a return value of 0 represents an error. Any other return value is a handle to the library. All that the code needs to do is verify that hLib contains a 0 in this example. It will, so the code that follows will execute.

Getting an error message is a two-step process. First, you have to retrieve the error number using GetLastError(). Of course, a number isn't particularly helpful to the end user, so you need to change the error number into a string using the FormatMessage() function. The FormatMessage() function is used for a variety of tasks, so it looks overly complex. However, when you create error message strings, the code shown in Listing 3.4 is generally all you need to provide. You have to tell FormatMessage() how to format the message using a number of flags. The arguments have to include the error number, and you need to provide a buffer to store the error string. Figure 3.3 shows the output from this example.

Helpful Win32 Programming Tools

Visual Studio .NET comes with a variety of tools as part of the package. In many cases, developers will already know about these tools because they'll use them to debug and validate existing applications. However, many of the tools take on special meaning when you develop applications that rely upon the Win32 API. These tools can help you discover some of the behaviors of the Win32 API and information that might not appear in the Visual Studio documentation.

We'll work with several of these tools as the book progresses. However, three tools are exceptionally important when you begin working with the Win32 API from the managed environment.

Dependency Walker This utility helps you learn about the contents of unmanaged DLLs on your system—both imports and exports. In addition, it helps you understand the relationship between various DLLs.

Error Lookup This utility enables you to decipher the error numbers returned by many of the Win32 API functions. The error numbers usually don't provide much information, but Error Lookup helps them make sense.

Spy++ This utility makes it possible to spy on your application—to see what the application is doing while running. While Spy++ doesn't provide the detailed information that a debugger would provide, it does help you see the application from the Windows perspective. This view often helps reduce the complexity of learning about the Win32 API.

Now that you know a little more about the utilities, let's look at them in depth. The following sections discuss each of the three utilities. We'll pay special attention to how the utilities can make it easier to work with the Win32 API. Of course, you'll also learn some general usage tips as the section progresses.

Was this article helpful?

0 0

Post a comment