Saturday, June 14, 2008 2:56 PM bart

LINQ to MSI - Part 1 bis - Interop with SafeHandles

A rather unexpected intermezzo in this series. Why? The first rule of blogging is that blog readers are always right, and this time it was no different. Although I pointed out yesterday "We're just doing raw interop here without more fancy SafeHandle stuff or so, but feel free to fine-tune the interop layer." there's no way for me to answer for example Steven's question:

Bart, Can you explain why to chose to implement finalizer methods on your classes instead of using a from SafeHandle inherited class to close the handle?

apart from the lazy answer "It's only a sample, dude!". But I definitely agree, quality needs to come first especially when you have the luxury to sit down and write the code in a relaxed atmosphere (as opposed to on-the-fly coding in an on-stage setting at some conference, not that such a setting wouldn't be relaxed of course...). I'm not completely guilty though, since I promised myself to fix this fit-and-finish issue by the time I'd publish the sources for this series. I was under the impression to have written about SafeHandles somewhere in the Whidbey timeframe but couldn't find a trace of it, so likely it never grew out of the draft stage. Therefore, this looks like an ideal opportunity to talk a bit about SafeHandles.

 

What's a SafeHandle?

The pattern I used in my yesterday's post is an old-fashioned one. The IDisposable part of it is still relevant and allows you to dispose off resources in a controllable fashion, e.g. by means of a using block in C# or VB. The reason I call it controllable is the fact you don't rely on the garbage collector to clean up the object, using the finalizer (the thing with destructor syntax in C# which was a little unfortunate choice of syntax). The fact I still have a finalizer implemented is the old-fashioned part of it. Why is that? In short, there are too little guarantees provided by finalizers (and the runtime having the task of calling them) to ensure reliability in all circumstances. During the v2.0 "Whidbey" timeframe, the .NET Framework team fixed this and there was the lucky coincidence of SQL Server 2005 "Yukon" looking into running managed code inside the server for what later became SQLCLR, by hosting the CLR inside the server process which obviously needs to provide strong guarantees about the stability and overall behavior of the CLR and managed code being run by it.

This whole reliability requirement isn't just realized through one single feature. Indeed, there are many pieces working together to establish this:

  • Host Protection Attributes - categorizes functionality of frameworks so that hosts like SQL Server can allow or disallow certain categories of code to be executed (e.g. no WinForms in SQLCLR)
  • Critical Finalizers - ensure that the finalizer gets executed properly, even under extreme circumstances (guaranteed to run during app domain unload, which means app domains can't leak handles and can be used as recycle units in case the application - e.g. SQL Server - gets into troubles)
  • Constrained Execution Regions - regions of code with guarantees that no asynchronous exceptions will be thrown by the runtime (the runtime ensures those will only happen before or after the region); a CER also puts strong restrictions on the developer about what can be used inside the region (which makes it hard to use them correctly directly), therefore providing more control over what specific pieces of code do

The core problem with old-fashioned IntPtr-based interop is the fact handles could be leaked in case asynchronous exceptions occur (an example is a rude thread abort causing ThreadAbortException). In addition to this, there's also a security risk by means of so-called recycle attacks. To understand this issue, you need to know that handles are just integer numbers kept on a per-process basis, pointing at some object maintained by the system. Once a handle is freed, that number becomes available again to point at some other resource, so once a new handle is obtained by some other function call it might well get the same number. If the old handle value is still kept around, incorrectly assuming it still points at the original resource, it now points at a possibly security-sensitive resource. This is another argument why it's a good practice (amongst others) to pair every call to CloseHandle with a zero-out assignment on the handle being closed (operating on an invalid handle also has the chance to surface obscure problems earlier than operating on a stale handle value that in the meantime might point to something else).

Finally, SafeHandles are implemented using Critical Finalizers and hence provide a way to ensure proper clean-up. The essence of a SafeHandle is to act as a wrapper for an IntPtr but one which the CLR knows about, so that the runtime can do the right thing to release the handle (not surprisingly there's a ReleaseHandle method).

I can't possibly cover all details on those features here and others have done it already, so if you want to learn more, here are some good pointers:

 

MsiSafeHandle

Here's a dump of an MsiSafeHandle implementation, deriving from SafeHandleZeroOrMinusOneIsInvalid (meaning that IntPtr values wrapped by it are invalid when they are -1 or 0, something implemented inside the IsInvalid property getter).

[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
internal class MsiSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
     private MsiSafeHandle() : base(true) /* take ownership of the handle */
     {
     }

     [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
     override protected bool ReleaseHandle()
     {
         return 0 == MsiInterop.MsiCloseHandle(handle);
     }
}

The CLR knows that whenever this class is used in an interop signature, it can create an instance of this class, adding the IntPtr handle value to it. When the runtime (or the application itself by calling Dispose on the object explicitly) decides the handle needs to be cleaned up, it calls ReleaseHandle which we implement by calling our interop function. Notice the ReliabilityContract attribute as well, which is part of CER (System.Runtime.ConstrainedExecution namespace), exposing the guarantees the method implementation makes (something that can't be enforced by the runtime and is still the responsibility of the developer).

 

Revised interop signatures

For our interop class, some changes are required to use the MsiSafeHandle class instead of IntPtr and to attribute the CloseHandle method with a CER attribute:

[SuppressUnmanagedCodeSecurity]
internal static class MsiInterop
{
     [DllImport("msi.dll", EntryPoint="MsiOpenDatabaseW", CharSet=CharSet.Unicode, ExactSpelling=true)]
     public static extern uint MsiOpenDatabase(string databasePath, int persist, out MsiSafeHandle database);

     [DllImport("msi.dll", EntryPoint="MsiDatabaseOpenViewW", CharSet=CharSet.Unicode, ExactSpelling=true)]
     public static extern uint MsiDatabaseOpenView(MsiSafeHandle database, string query, out MsiSafeHandle view);

     [DllImport("msi.dll", EntryPoint="MsiViewExecute", CharSet=CharSet.Unicode, ExactSpelling=true)]
     public static extern uint MsiViewExecute(MsiSafeHandle view, MsiSafeHandle parameters);

     [DllImport("msi.dll", EntryPoint="MsiViewFetch", CharSet=CharSet.Unicode, ExactSpelling=true)]
     public static extern uint MsiViewFetch(MsiSafeHandle view, out MsiSafeHandle record);

     [DllImport("msi.dll", EntryPoint="MsiRecordGetStringW", CharSet=CharSet.Unicode, ExactSpelling=true)]
     public static extern uint MsiRecordGetString(MsiSafeHandle record, uint field, StringBuilder value, ref int bufferSize);

     [DllImport("msi.dll", EntryPoint="MsiRecordGetInteger", CharSet=CharSet.Unicode, ExactSpelling=true)]
     public static extern int MsiRecordGetInteger(MsiSafeHandle record, uint field);

     [DllImport("msi.dll", EntryPoint="MsiCloseHandle", CharSet=CharSet.Unicode, ExactSpelling=true)]
     [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
     public static extern uint MsiCloseHandle(IntPtr handle);
}

Notice the MsiCloseHandle function is the only one which still takes an IntPtr. This is required because we still need to call this function directly inside our MsiSafeHandle class. However, other than in that particular spot, direct calls to MsiCloseHandle shouldn't be made anymore, since the handle will always be wrapped inside an MsiSafeHandle which implements IDisposable, ultimately calling into ReleaseHandle to do the heavy work.

 

Using it

I'll just give one sample here, based on our MsiConnection class. Other classes can be changed completely analogously:

public sealed class MsiConnection : IDisposable
{
     private string _fileName;
     private MsiSafeHandle _database;
     private bool _disposed;

     public MsiConnection(string fileName)
     {
          if (!File.Exists(fileName))
          {
               throw new FileNotFoundException(fileName + " not found.");
          }

          _fileName = fileName;
     }

     public void Open()
     {
          CheckDisposed();

          uint res = MsiInterop.MsiOpenDatabase(_fileName, 0, out _database);
          if (res != 0)
          {
               throw new Exception("Failed to open database. Error code = " + res);
          }
     }

     internal MsiSafeHandle Handle
     {
          get { CheckDisposed(); return _database; }
     }

     public void Dispose()
     {
          Dispose(true);
          GC.SuppressFinalize(this);
     }

     private void Dispose(bool disposing)
     {
          if (!_disposed && _database != null && !_database.IsInvalid)
          {
               _database.Dispose();
          }

          _disposed = true;
     }

     private void CheckDisposed()
     {
          if (_disposed)
              throw new ObjectDisposedException("MsiConnection");
     }
}

Notice we still are IDisposable, nothing's changed there. However, now we're storing MsiSafeHandle instead of an IntPtr which gets disposed by our Dispose method (but also automatically during critical finalization, so we get the guarantees we've always wanted). Depending on the class design you prefer for the Msi* classes, the helper Dispose method would either be private or protected virtual. In this case, I'm sealing the class which means the helper method should be private. If you were to choose otherwise, the helper method should be protected virtual and be decorated with a Demand for unmanaged code security permissions.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:

Comments

# LINQ to MSI - Part 1 bis - Interop with SafeHandles

Saturday, June 14, 2008 5:10 PM by LINQ to MSI - Part 1 bis - Interop with SafeHandles

Pingback from  LINQ to MSI - Part 1 bis - Interop with SafeHandles

# All Else Failed » LINQ to MSI - Part 1 bis - Interop with SafeHandles

Pingback from  All Else Failed » LINQ to MSI - Part 1 bis - Interop with SafeHandles

# All Else Failed » LINQ to MSI - Part 1 bis - Interop with SafeHandles [B# .NET Blog]

Pingback from  All Else Failed » LINQ to MSI - Part 1 bis - Interop with SafeHandles [B# .NET Blog]

# All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with …

Pingback from  All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with …

# All Else Failed » All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with …

Pingback from  All Else Failed » All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with …

# All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with ???

Pingback from  All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with ???

# All Else Failed ?? All Else Failed ?? LINQ to MSI - Part 1 bis …

Pingback from  All Else Failed ?? All Else Failed ?? LINQ to MSI - Part 1 bis …

# All Else Failed » All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with ???

Pingback from  All Else Failed » All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with ???

# All Else Failed » All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with ???

Pingback from  All Else Failed » All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with ???

# All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with ???

Pingback from  All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with ???

# All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with SafeHandles

Pingback from  All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with SafeHandles

# All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with SafeHandles

Pingback from  All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with SafeHandles

# All Else Failed ?? All Else Failed ?? LINQ to MSI - Part 1 bis ???

Pingback from  All Else Failed ?? All Else Failed ?? LINQ to MSI - Part 1 bis ???

# All Else Failed » All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with SafeHandles

Pingback from  All Else Failed » All Else Failed ?? LINQ to MSI - Part 1 bis - Interop with SafeHandles

# All Else Failed ?? All Else Failed ?? LINQ to MSI - Part 1 bis ???

Pingback from  All Else Failed ?? All Else Failed ?? LINQ to MSI - Part 1 bis ???

# All Else Failed » All Else Failed ?? All Else Failed ?? LINQ to MSI - Part 1 bis ???

Pingback from  All Else Failed » All Else Failed ?? All Else Failed ?? LINQ to MSI - Part 1 bis ???

# All Else Failed ?? All Else Failed ?? All Else Failed ?? LINQ to …

Pingback from  All Else Failed ?? All Else Failed ?? All Else Failed ?? LINQ to …

# LINQ to MSI

Friday, July 25, 2008 10:51 AM by InstallSite Blog

LINQ stands for Language-Integrated Query and enables you to directly query databases in .NET programming