Friday, July 31, 2009

DllRegisterServer w/o regsvr32

I have a need to register some COM DLLs from a C# program. Every example I found on the web used regsvr32 to perform the registration. This didn't make much sense to me as all regsvr32 does is call DllRegisterServer() exported in the DLL. So I worked out how to do the same from C# using P/Inovke and System.Runtime.InteropServices.

And here it is for you to use. This is a small sample but it compiles and registers the DLLs that are listed on the command line. Be sure to compile with the /unsafe switch. Also, I have more error checking and reporting in my application which I removed from this sample. Enjoy!

   1:  using System;
   2:  using System.Runtime.InteropServices;
   4:  public class RegisterDll
   5:  {
   6:      unsafe internal delegate UInt32 DllRegisterServer();
   8:      public static void Main(string[] args)
   9:      {
  10:          foreach (string file in args)
  11:          {
  12:              Console.WriteLine("Registering {0}", file);
  13:              RegisterCOMDll(file);
  14:          }
  16:          Console.WriteLine("Done!");
  17:      }
  19:      private static void RegisterCOMDll(string file)
  20:      {
  21:          IntPtr library = LoadLibraryEx(file, IntPtr.Zero, 0);
  22:          if (library == IntPtr.Zero)
  23:          {
  24:              string msg = "Unable to load '" + file + "' error is " + Marshal.GetLastWin32Error().ToString();
  25:              throw new ApplicationException(msg);
  26:          }
  28:          IntPtr proc = GetProcAddress(library, "DllRegisterServer");
  29:          if (proc == IntPtr.Zero)
  30:          {
  31:              int err = Marshal.GetLastWin32Error();
  32:              string msg = "Unable to load 'DllRegisterServer' from '" + file + "' error is " + err.ToString();
  33:              throw new ApplicationException(msg);
  34:          }
  36:          DllRegisterServer drs = (DllRegisterServer)Marshal.GetDelegateForFunctionPointer(proc, typeof(DllRegisterServer));
  37:          UInt32 result = drs();
  38:          if (result != 0)
  39:          {
  40:              string msg = "Error " + result.ToString() + " returned from DllRegisterServer in " + file;
  41:              throw new ApplicationException(msg);
  42:          }
  44:          FreeLibrary(library);
  45:      }
  47:      [Flags]
  48:      public enum LoadLibraryFlags : uint
  49:      {
  50:          DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
  51:          LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
  52:          LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
  53:          LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008,
  54:      }
  56:      [DllImport("kernel32.dll", EntryPoint = "FreeLibrary", SetLastError = true)]
  57:      public static extern bool FreeLibrary(IntPtr hModule);
  59:      [DllImport("kernel32.dll", EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode, SetLastError = true)]
  60:      public static extern IntPtr LoadLibraryEx(
  61:          [MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
  62:          IntPtr hFile,
  63:          [MarshalAs(UnmanagedType.U4)] LoadLibraryFlags dwFlags);
  65:      [DllImport("kernel32.dll", CharSet = CharSet.Ansi, EntryPoint = "GetProcAddress", ExactSpelling = true, SetLastError = true)]
  66:      public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
  67:  }

Thursday, March 26, 2009

KioskCrash: It goes *BOOM*!

Today I'm going to cover what happens when the KioskCrash application crashes. We'll cover what information the operating system provides to help diagnose the crash and what we can use to help diagnose the crash before we add any debugging support to our application. So without further ado...

When KioskCrash crashes Windows XP will display a window similar to this. There really isn't much information here, just a button to automatically send an error report to Microsoft and another button to skip sending the report. If you click the "Send Error Report" button the operating system will bundle up a bit of information about the crash and send it off to Microsoft for diagnosis. This window also includes a link to see what data the error report contains.

This is an example of what you would see with if you clicked the link to see what data is contained in the error report. The first section is the only interesting thing displayed in this window. It contains the application name & version, the module name & version and the location where the crash occurred in our image. This is why you should always add a version information resource in executables you create. Since KioskCrash is a simple application I only filled in basic information. In a large application this information would be invaluable for locating the crash. The location of the crash, called "Offset" in this window, can be used to get an idea where to find the problem in your code. I will go into more detail about how to do that in a later post. (Probably the next one.) The only other interesting thing displayed in this window is a link at the bottom to see more technical information about the error report.

Now we get to the details of the exception. The important things to note in this window is the type of exception (0xC0000005 Access Violation) and the address of the exception (0x401016). Using the image base address we can calculate that the offending instruction was located at offset 0x1016 in our module. This is about all the information we can extract from the error report windows that are displayed by the operating system.

To recap the information we've been able to determine from the Windows XP error report is that version of the KioskCrash application crashed because of an access violation at offset 0x1016. This doesn't help us much because we don't have a way to match the source code to offset 0x1016. If we had a "map" file we might be able to determine which function caused the crash. With the symbols for this version of KioskCrash we could determine which function and perhaps even which line in that function crashed.

Next time I'll cover map files, how to create them as part of the build and how to use them with this information to locate the source of a crash. Until then...

Monday, February 9, 2009

Testing Google Code Prettyfier

I'm testing using Google's code syntax highlighter. So here's a simple C++ function to see the results.

int fact(int n)
    // quickly return the simple terminal case
    if (n <= 1)
        return 1.0;

    return n * fact(n - 1);

Microsoft Visual C++ 2008 Redistributable

The KioskCrash executable in my last post was created using Microsoft Visual C++ 2008. I forgot to include links to the redistributable which needs to be installed on systems without VC++ 2008.

Sorry about that folks.

Wednesday, February 4, 2009

KioskCrash In the Wild!

I'm pleased to announced the release of KioskCrash v1.0.0. This is the sample application that crashes in a variety of repeatable and predictable ways. From this base I will be demonstrating how to capture diagnostic information from a crash and how to use that information to find and fix problems.

You can download the binary and source code at my Google Code project site. There is also a Subversion repository if you would rather use that. The most current source can be retrieved from:

You can see a screenshot of KioskCrash running above. Just type the number of the crash you wish to create and watch the fireworks. Below is the function that generates a read access violation. I create an invalid pointer to an integer and then try to read the value. The last three lines just display a message in the event our crash doesn't occur. It’s simple but my goal here isn’t to write pretty code to crash your system.

void generate_read_access_violation()
   int *bad = 0;
   int value = *bad;

   TCHAR msg[1024];
   StringCbPrintf(msg, sizeof(msg),
                  _T("The integer at address 0 is %d"), value);
   MessageBox(GetDesktopWindow(), msg, _T(__FUNCTION__),
              MB_OK | MB_ICONINFORMATION);

That’s all I have for this post. Next time I'll talk about what happens when an application crashes and what information we can gather to help with diagnosis.

Oh, tonight's beer is Bete Noel from Elysian Brewing. A very tasty Belgian style ale. The description from Elysian is:

The dark side of the holidays, as treacherous as its paler sister. Brewed with Maris-Otter pale, Weyermann Munich, Castle Aromatic and roasted barley and black malts, with amber Belgian candi sugar added to the kettle. Bittered with German Northern Brewer, finished with Czech Saaz hops and fermented with Belgian Golden Ale yeast.

Thursday, January 29, 2009

KioskCrash: Introduction

Welcome to Leber Hall! I want this blog to be interactive and a learning experience for us all. So don’t feel shy and go ahead and ask questions and clarifications. I am going to try and walk the line between skipping too many details and plodding along boring us all with too much information. These posts will get quite technical at times, but I will try to keep the information accessible so neophyte programmers can get some benefit my writing.

The first series of posts I’m going to write will cover ways to find a crash in an unattended kiosk application. My plan is to present a sample program that will crash. Over time I will update the sample with the techniques being discussed. We will explore various options for finding the crash and why particular choices were made.

In this series an “unattended application” is a program that runs on a kiosk that doesn’t have a keyboard, mouse or any other traditional user interface devices. While there are “users” who use the kiosk they don’t have access to technical support, web browsers or email to report problems and work with someone to fix the problem. At best when the application crashes the kiosk is able to recover and restart automatically. The next best option is to display an “out of service” message to the user. The worst option is to display error messages to the user and wait for their input.

I will be using Microsoft Windows XP Pro SP3 and Visual C++ 2008 Professional Edition while writing these articles. If you would like to follow along I believe that you could use other versions of Windows and the free Visual C++ 2008 Express Edition. You will need the Debugging Tools for Windows, also free, when we start diagnosing the crash reports.

What is a crash? In simple terms it is an invalid operation that causes the OS to stop a program from executing. The most common types of crashes I’ve encountered are caused by attempting to read or write to invalid memory, overflowing the stack or dividing by zero. The Platform SDK for Windows defines 23 codes that can be generated by a hardware exception. I won’t explain them all. Read the documentation for EXCEPTION_RECORD if you would like to know more.

What’s the difference between a crash and an exception? Depending on what kind of exception you mean there isn’t any difference at all. Windows uses a mechanism called Structured Exception Handling (SEH) to handle both hardware and software exceptions. C++ has exception handling as a language feature. C++ exceptions are not the same as SEH exceptions and I’ll need to discuss both kinds as I go along. To distinguish between the two different exception types I will call the event that causes the OS to kill a running program a crash. I will use the Structured Exception Handling features provided by Windows to capture the state of the application. C++ exception handling will also be used to provide a mechanism to recover from some types of crashes.

Ok, that’s enough rambling for now. Next time I’ll present the sample application that I’ll use as a base for this series of articles.

Open the Doors and Tap the Keg!

Welcome to Leber Hall. Here I will write about programming topics that have been rolling around in my head for a while. In my day job I work as a C++ programmer on an kiosk application that runs on an Windows XP Embedded system. At night my interests range a lot farther afield and I play with languages like C#, scheme, and Perl. So I expect the topics to range over the map as it strikes my fancy.

The name Leber Hall is a reference to my name and the first business my ancestors opened when the emigrated to the US from Germany. Remig Leber did the world a great service by opening a pub in Pacific, Missouri. I enjoy beer and pubs so why not follow the family tradition where we can sit around, enjoy good drink and talk about geeky subjects?

Welcome everyone! First round's on the house!