//
you're reading...
Programming Issues/Tips

Using the SafeHandle Class to Customize the Management of Unmanaged Handles.

1. Introduction.

1.1 Recently (circa March 2012) I became interested to see how we can use managed SafeHandle-derived classes to wrap various Operating System handles for customized management.

1.2 I thus began to do research on this subject and eventually emerged with this article and the accompanying sample codes.

1.3 I hope that this article will spark further study and contributions from other developers out there who may be interested in this subject or who have have already done similar work.

1.4 In this current article, I aim to show how we can wrap the OS event object in a SafeHandle-derived class.

2. The General Use-Case Scenario.

2.1 Let’s say we have a time-consuming process which is performed in unmanaged code in a DLL written in C++.

2.2 Further assume that the DLL exposes 3 APIs :

  • InitializeTimeConsumingProcess()
  • PerformTimeConsumingProcess()
  • UninitializeTimeConsumingProcess()

2.3 The first API InitializeTimeConsumingProcess() serves as a typical general initialization routine to be called once per application. This API also serves to create an OS event object with a well-known name. This event object can then be accessed publicly by interested applications.

2.4 The second API PerformTimeConsumingProcess() starts the time-consuming process and then returns immediately. The time consuming process is then performed asynchronusly in a separate thread. When the time consuming process completes, the event object created in earlier is then set.

2.5 The third API UninitializeTimeConsumingProcess() performs a general shutdown operation. Inside this function, the OS event object is destroyed.

2.6 It must be noted that the above scheme of lengthy process initialization, startup and notification of its completion is very simple. It certainly does not measure up to industrial strength robustness.

2.7 Its design is certainly questionable. For example, what happens if, when InitializeTimeConsumingProcess() is called by a client, the well-known named OS event object has already been created by another process using the same DLL ? Does it acquire the event object (using an API like OpenEvent()) ? What happens if, when PerformTimeConsumingProcess() is called by a client, it has previously been called by another client ?

2.8 We will not address these issues because this set of APIs are highly contrived and are specifically designed for demonstration only.

2.9 Turning now to the client application side. Let’s say we have a C# client application that is interested in activating the time consuming process and in receiving notice when the process ends.

2.10 This C# client would have to import the 3 APIs listed above.

2.11 Like any client application, it must first call InitializeTimeConsumingProcess() to initialize the DLL.

2.12 After that, it must acquire the OS event object with the well-known name that is created by the InitializeTimeConsumingProcess() API. This is where our first encounter with a SafeHandle-derived class comes in.

2.13 After acquiring the OS event object and wrapping it inside a SafeHandle-derived class instance, the client application will call on PerformTimeConsumingProcess() to begin performing the lengthy operation. It will then wait on the OS event object until it is signaled.

2.14 The client application eventually calls UninitializeTimeConsumingProcess() to complete the whole process.

3. Implementation of the 3 Main APIs.

3.1 In this section, we present the implementation codes for the 3 APIs descussed in the previous section.

3.2 Listed below is the source code for InitializeTimeConsumingProcess() :

HANDLE g_hTimeConsumingProcess = NULL;

extern "C" __declspec(dllexport) void __stdcall InitializeTimeConsumingProcess()
{
	if (g_hTimeConsumingProcess == NULL)
	{
		g_hTimeConsumingProcess = CreateEvent(NULL, TRUE, FALSE, L"TIME_CONSUMER");
	}
}

Here are some noteworthy points about the code above :

  • A global HANDLE named “g_hTimeConsumingProcess” is declared and set to NULL.
  • InitializeTimeConsumingProcess() checks to see if “g_hTimeConsumingProcess” is NULL. Only if so will it proceed to call the CreateEvent() API to create a named OS event object.
  • The name given is “TIME_CONSUMER”.

3.3 Listed below is the source code for PerformTimeConsumingProcess() :

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	HANDLE hEvent = (HANDLE)lpParameter;

	Sleep(10000);

	SetEvent(hEvent);

	return 0;
}

extern "C" __declspec(dllexport) void __stdcall PerformTimeConsumingProcess()
{
	HANDLE hThread = CreateThread
	(
		NULL,
		0,
		ThreadProc,
		(LPVOID)g_hTimeConsumingProcess,
		0,
		NULL
	);

	if (hThread)
	{
		CloseHandle(hThread);
		hThread = NULL;
	}
}

Here are some noteworthy points about the code above :

  • PerformTimeConsumingProcess() creates a thread (fronted by ThreadProc()).
  • The event handle “g_hTimeConsumingProcess” is passed as a parameter.
  • After creating the thread, it closes the thread handle and returns.
  • Because it starts the ThreadProc() thread and returns, PerformTimeConsumingProcess() does not cause its caller to block.
  • The time consuming process (done in ThreadProc()) is performed asynchronously.
  • ThreadProc() converts its parameter into a handle and then sleeps for 10 seconds.
  • After 10 seconds, it sets the event handle.

3.4 Listed below is the source code for the UninitializeTimeConsumingProcess() API :

extern "C" __declspec(dllexport) void __stdcall UninitializeTimeConsumingProcess()
{
	if (g_hTimeConsumingProcess != NULL)
	{
		CloseHandle(g_hTimeConsumingProcess);
		g_hTimeConsumingProcess = NULL;
	}
}

It is a very simple function which simply closes the event handle “g_hTimeConsumingProcess” provided it is not NULL.

3.5 For demonstrative purposes I have named the DLL “HelperDLL.dll”. 

4. C# Client Code.

4.1 This section will cover many issues about the C# client code. We will address the following topics :

  • How to declare the 3 main APIs (via the DllImportAttribute) for use in C#.
  • How to acquire the OS event object which has been named “TIME_CONSUMER”.
  • When acquired, what data type should the event object be stored as.
  • This leads us directly to the design and implementation of our SafeHandle-derived wrapper class.

4.2 Declaration of the 3 main APIs.

The following code shows how the 3 APIs can be declared in a C# source :

[DllImport("HelperDLL.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void InitializeTimeConsumingProcess();

[DllImport("HelperDLL.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void PerformTimeConsumingProcess();

[DllImport("HelperDLL.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void UninitializeTimeConsumingProcess();

4.3 Acquiring the “TIME_CONSUMER” Event Object.

For this, we use the OpenEvent() API which is exported from Kernel32.dll. Now, a typical way to declare such an API in C# would be :

[DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet=CharSet.Unicode)]
private static extern IntPtr OpenEvent
    (
      [In] UInt32 dwDesiredAccess,
      [In][MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
      [In][MarshalAs(UnmanagedType.LPWStr)] string lpName
    );

Note the following important points about the code above :

  • The OpenEvent() API returns a HANDLE. This is normally represented in C# as IntPtr (as shown above).
  • However, our aim in this article is to represent unmanaged HANDLEs using a SafeHandle-derived C# class.
  • To do so, we must set the return type for OpenEvent() to be the SafeHandle-derived C# class.
  • I will provide the alternative declaration for OpenEvent() after I have touched on our special wrapper class next.

4.4 SafeEventHandle – Our SafeHandle-Derived Wrapper Class.

Here in this sub-section, I will present the SafeEventHandle class which serves as a wrapper for the OS event object returned from a call to OpenEvent().

The SafeEventHandle class is designed to be a container class that will wrap the handle of a Win32 OS event object. It is derived from the SafeHandle class. The basic motivation is to provide a class that demonstrates how to manage such a Win32 object through participation in the framework.

As we know, the primary purpose of a Win32 OS event object is to synchronize threads. Typical operations involving the use of Win32 event objects in managed code will include the cooperation of the ManualResetEvent class and the SafeWaitHandle class. The SafeEventHandle that we developed will demonstrate how it facilitates this.

4.5 SafeEventHandle – Source Codes.

Full source codes presented below :

internal class SafeEventHandle : SafeHandle
{
    [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CloseHandle([In] IntPtr hObject);

    // Create a SafeHandle, informing the base class
    // that this SafeHandle-derived class instance 
    // "owns" the handle, and therefore the SafeHandle 
    // base class sub-object should call our ReleaseHandle()
    // method when the SafeHandle is no longer in use.
    private SafeEventHandle()
        : base(IntPtr.Zero, true)
    {
        handle = IntPtr.Zero;
    }

    // The ReleaseHandle() method is guaranteed to be called only once 
    // and only if the handle is valid as defined by the IsInvalid property.
    override protected bool ReleaseHandle()
    {
        return CloseHandle(handle);
    }

    // The IsInvalid property must be overridden by a SafeHandle-derived
    // class. This property prosents the derived-class with the opportunity
    // to determine whether the handle it manages is valid or not.
    override public bool IsInvalid 
    {
        get
        {
            if (handle == IntPtr.Zero)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    // Additional helper function that returns the underlying
    // handle that this class manages. It calls DangerousAddRef()
    // which manually increments the reference for the handle.
    //
    // A call to GetHandle() must be matched with a call to
    // ReturnHandle().
    public IntPtr GetHandle()
    {
        if (IsInvalid)
        {
            throw new Exception("The handle is invalid.");
        }
        else
        {
            bool bSuccess = false;
            DangerousAddRef(ref bSuccess);
            return handle;
        }
    }

    // Additional helper function that decrements the handle
    // count of the underlying handle that this class manages.
    public void ReturnHandle(IntPtr h)
    {
        if ((h == handle) && (!IsInvalid))
        {
            DangerousRelease();
        }
    }

    // A cast operator that casts an instance of this class
    // into a SafeWaitHandle object. The returned SafeWaitHandle
    // can be used in waiting operations with the help of a
    // ManualResetEvent object.
    static public implicit operator SafeWaitHandle(SafeEventHandle safe_event_handle)
    {
        if (safe_event_handle.IsInvalid)
        {
            throw new Exception("The handle is invalid.");
        }
        else
        {
            return new SafeWaitHandle(safe_event_handle.handle, false);
        }
    }
}

The following are some important points about the code above :

  • The public default constructor of SafeEventHandle is declared as private. This is so that it cannot be instantiated by using the new keyword. We will discuss how it is instantiated later.
  • However, when it is instantiated, the constructor will call the base class’ constructor which takes 2 parameters : one to set the value which the base class will recognize as indicating an invalid handle value and the other to indicate whether the current derived class (i.e the SafeEventHandle class) should own the internal handle.
  • The SafeEventHandle class will take the opportunity in the constructor to indicate that it will own the internal handle and to set the value of its handle member to IntPtr.Zero.
  • Later on, the CLR will set the value for the internal handle member.
  • There are 2 SafeHandle members which derived classes must override : the ReleaseHandle() method and the IsInvalid (boolean) property.
  • The SafeEventHandle.ReleaseHandle() member calls the CloseHandle() Windows API on its internal handle value. We shall discuss this in more detail when we study how the framework interacts with the SafeEventHandle class late on (see section 5).
  • The IsInvalid property is an important property and it returns a boolean value according to the current value of its “handle” property. The IsInvalid property is used by the framework to ascertain the validity of a SafeHandle-derived class. More on this when we examine example codes later (see section 5).
  • The GetHandle(), ReturnHandle() functions and the SafeWaitHandle() implicit operator are helper functions which we shall cover in greater detail later through example codes.

4.6 Alternative Declaration for OpenEvent() API.

Now that the SafeWaitHandle class has been properly defined, we can use it as the return type for the OpenEvent() API :

const UInt32 SYNCHRONIZE = 0x00100000;
const UInt32 EVENT_MODIFY_STATE = 0x0002;

[DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet=CharSet.Unicode)]
private static extern SafeEventHandle OpenEvent
    (
      [In] UInt32 dwDesiredAccess,
      [In][MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
      [In][MarshalAs(UnmanagedType.LPWStr)] string lpName
    );

The SYNCHRONIZE and EVENT_MODIFY_STATE constant values are used in specifying the value for the first parameter dwDesiredAccess.

Note that the return type for OpenEvent() can indeed be set to SafeEventHandle and the CLR will construct one when the API returns.

5. How SafeWaitHandle Participates in the Framework.

5.1 In this section, we examine how an instance of the SafeWaitHandle class participates in the framework.

5.2 We will do so with the help of a simple user function :

private static void SimpleUsageTest()
{
    InitializeTimeConsumingProcess();

    using (SafeEventHandle hEvent = OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, false, "TIME_CONSUMER"))
    {
    }

    UninitializeTimeConsumingProcess();
}

The following is a summary of what the code above does :

  • It first calls InitializeTimeConsumingProcess() to initialize the “TIME_CONSUMER” Win32 event object.
  • After that, the OpenEvent() API is called.
  • A new instance of the SafeWaitHandle class is constructed and then returned.
  • Now, because we have wrapped the call to OpenEvent() in a “using” statement, the SafeWaitHandle instance “hEvent” will be immediately disposed at the end of the statement.
  • After that, UninitializeTimeConsumingProcess() is called to close the Win32 event object handle.

5.3 Now, when OpenEvent() completes, a Win32 event object handle will be returned. This is where the framework will first instantiate the SafeEventHandle class :

private SafeEventHandle()
    : base(IntPtr.Zero, true)
{
    handle = IntPtr.Zero;
}

After the constructor has been invoked, the framework will set SafeEventHandle.handle to the value returned from OpenEvent(). 

Now, if, for some reason, the value returned from OpenEvent() is IntPtr.Zero (i.e. a NULL value), the “IsInvalid” property will immediately be set to “true”.

5.4 The body of the “using” statement is empty and so there is no interaction with the SafeEventHandle instance “hEvent”.

5.5 However, when the “using” statement completes, “hEvent” will go out of scope and so the framework will call the Dispose() method of the parent SafeHandle class. The “IsInvalid” property of the derived SafeEventHandle class is accessed :

override public bool IsInvalid 
{
    get
    {
        if (handle == IntPtr.Zero)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

Here is where a SafeHandle-derived class like SafeEventHandle has a chance to determine whether the current state of an instance is such that it can be considered valid. This state is completely dependent on the class implementation and may include (but is not limited to) the current value of its internal handle.

In the case of SafeEventHandle, the value of its internal handle is the sole factor in determining validity. Other implementations may lookup other variables or perform various complex operations (e.g. reading a file, make an unmanaged DLL function call, etc).

5.6 Only if “IsInvalid” returns false will the ReleaseHandle() member function be called by the framework :

override protected bool ReleaseHandle()
{
    return CloseHandle(handle);
}

SafeEventHandle will call the Win32 API CloseHandle() to close the internal handle.

Again, other implementations of a SafeHandle-derived class may perform any variety of operations. For example, a class that wraps the HANDLE returned from a call to OpenEventLog() may call CloseEventLog(). Other implementations may want to write some entry into a log file or a database table, etc. 

6. How SafeEventHandle Is Used To Wait On An OS Event Object.

6.1 But of course, the whole purpose of the SafeEventHandle class is to expedite in the waiting of the OS event handle which it wraps.

6.2 In order to wait on a Win32 OS event object, we will require the services of the WaitHandle and the SafeHandle classes. We would usually use classes which are derived from them, e.g. ManualResetEvent (derived from WaitHandle) and SafeWaitHandle (derived from SafeHandle).

6.3 Using ManualResetEvent and SafeWaitHandle, the general pattern of calls is as follows :

ManualResetEvent manual_reset_event = new ManualResetEvent(false);
IntPtr SomeHandle = SomehowGetHandle();
manual_reset_event.SafeWaitHandle = new SafeWaitHandle(SomeHandle, true);

if (manual_reset_event.WaitOne())
{
    manual_reset_event.Reset();
}

The following is a summary of the code above :

  • A ManualResetEvent object is created (manual_reset_event).
  • Its initial state is set to false meaning that the event that it will wait on is not signalled yet.
  • Then an OS event handle is retrieved by some fictitious function SomehowGetHandle().
  • The event handle is stored inside an IntPtr “SomeHandle”.
  • The SafeWaitHandle property of the ManualResetEvent object is set to a new instance of a SafeWaitHandle.
  • This step is necessary because the ManualResetEvent object needs to wait on an actual OS waitable event object. A SafeWaitHandle object provides the abstraction for such a waitable event object.
  • The SafeWaitHandle object is constructed by being supplied with the existing event handle “SomeHandle” and being indicated that it will own the “SomeHandle” handle.
  • This ownership flag will indicate to the SafeWaitHandle object property of the ManualResetEvent object whether it should close the handle when it is disposed.
  • The wait function (e.g. WaitOne()) of the ManualResetEvent object can then be called to wait for the signalling of the event object.
  • Thereafter the Reset() function can be called to set the event object back to being non-signalled.

6.4 Following the above pattern of event waiting, there must then be a way for the SafeEventHandle class to return its internal “handle” property. That is, we want an equivalent of the SomehowGetHandle() function.

6.5 For this, we supply the SafeEventHandle.GetHandle() function :

public IntPtr GetHandle()
{
    if (IsInvalid)
    {
        throw new Exception("The handle is invalid.");
    }
    else
    {
        bool bSuccess = false;
        DangerousAddRef(ref bSuccess);
        return handle;
    }
}

The following are important points about the code above :

  • It first checks for validity of the entire SafeEventHandle instance.
  • If it is not valid, an exception is thrown.
  • Otherwise, it returns its internal handle.
  • Notice that before it returns its handle, it will call the strangely named DangerousAddRef() function.
  • This function increments the reference count of the handle to be returned.
  • This step is necessary because we are actually supplying the internal handle to some code.
  • This ensures that the handle is not inadvertently destroyed while some code somewhere is still referencing it.

6.6 On the same note, there must also be a conjugal function that decrements the reference count of a returned handle. For this, we supply the SafeEventHandle.ReturnHandle() function :

public void ReturnHandle(IntPtr h)
{
    if ((h == handle) && (!IsInvalid))
    {
        DangerousRelease();
    }
}

This function takes an IntPtr parameter that must equal the IntPtr that was returned from GetHandle(). GetHandle() will check that the “h” parameter indeed equals the internal handle value and also check for validity of the entire SafeEventHandle instance. If all goes well, DangerousRelease() is called.

DangerousRelease() will perform a decrementation of the reference count of the internal handle.

6.7 Based on the pattern presented in point 6.3 and the usage of the SafeEventHandle.GetHandle() and the SafeEventHandle.ReturnHandle() methods, we can emerge the following code for demonstrating how to wait on a Win32 OS event object using the SafeEventHandle class :

private static void ExampleWaitEvent01()
{
    InitializeTimeConsumingProcess();

    try
    {
        // Obtain a handle to the Win32 Event Object named as "TIME_CONSUMER"
        // and wrap it inside a SafeEventHandle object.
        SafeEventHandle hEvent = OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, false, "TIME_CONSUMER");

        // Start the time consuming process.
        PerformTimeConsumingProcess();

        // Create a ManualResetEvent object which is initially not set.
        ManualResetEvent manual_reset_event = new ManualResetEvent(false);
        // Obtain the internal handle contained inside hEvent.
        IntPtr hWaitHandle = hEvent.GetHandle();
        // Create a SafeWaitHandle object, set its handle value to that
        // which has been returned from hEvent.GetHandle() and set this
        // SafeWaitHandle object as the SafeWaitHandle property of 
        // manual_reset_event.
        manual_reset_event.SafeWaitHandle = new SafeWaitHandle(hWaitHandle, false);

        // Start the waiting process.
        if (manual_reset_event.WaitOne())
        {
            // Rsset the event.
            manual_reset_event.Reset();
        }

        // We must "return" the handle previously obtained
        // from "hEvent".
        hEvent.ReturnHandle(hWaitHandle);
        hWaitHandle = IntPtr.Zero;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

    UninitializeTimeConsumingProcess();
}

The following are pertinent points about the code above :

  • After invoking the InitializeTimeConsumingProcess() function, it proceeds to call OpenEvent() to acquire a Win32 event object handle.
  • The returned handle is wrapped inside an instance (“hEvent”) of a SafeEventHandle class.
  • The PerformTimeConsumingProcess() function is then called to start the time consuming operation.
  • A ManualResetEvent object is instantiated and initially set to non-signaled (via its boolean constructor parameter which is set to “false”).
  • Next, the GetHandle() method of the SaveEventHandle instance “hEvent” is called to obtain the internal handle value wrapped inside it.
  • This internal handle value is kept inside an IntPtr “hWaitHandle”.
  • Next, we need to set the SafeWaitHandle property of the ManualResetEvent object. This is set to a new instance of the SafeWaitHandle class constructed with “hWaitHandle” as the first parameter.
  • Now, notice that the second parameter (that indicates whether the SafeWaitHandle instance is to own the input handle) is set to “false”. This tells the SafeWaitHandle class not to attempt to release the input handle when it is disposed.
  • Why is this constructor parameter set to “false” ? The next point will explain this in full.

6.8 Why is this constructor parameter set to “false” ? In short : balance of reference counting. The following is a summary :

  • When OpenEvent() was called and an OS event handle is returne, a reference count is added to this handle by the OS (reference count : 1).
  • The handle is stored in a SafeEventHandle class instance which will later decrement the reference count when it releases the handle.
  • Then, when the internal handle of the SafeEventHandle instance is requested via GetHandle(), another reference count is added via DangerousAddRef() (reference count : 2).
  • When the handle is later “returned” to the SafeEventHandle instance via ReturnHandle(), DangerousRelease() is called on the handle. This decrements the reference count (reference count : 1).
  • Then, when the SafeEventHandle instance is disposed, its ReleaseHandle() method will be invoked by the .NET Framework. Here, CloseHandle() will be called on the handle. This further decrements the reference count (reference count : 0).
  • At this point, as far as the managed application is concerned, the handle has been fully released to the OS.
  • Now, if the handle was passed to the SafeWaitHandle instance with full ownership (second parameter set to “true”), then when the SafeWaitHandle instance is disposed of, it will release the handle (likely via a call to CloseHandle()).
  • Depending on whether the SafeWaitHandle instance’s or the SafeEventHandle instance’s Dispose() method is called first, one of the calls to release the handle will result in an attempt to release an already fully released handle.
  • This will result in an exception.

7. The SafeWaitHandle Implicit Conversion Operator – A Safer Way To Obtain A SafeWaitHandle.

7.1 It is, of course, possible that despite adequate documentation, client code may still instantiate a SafeWaitHandle with full onwership of the handle returned from SafeEventHandle.GetHandle().

7.2 To overcome this potential problem, I designed the SafeWaitHandle implicit conversion operator :

static public implicit operator SafeWaitHandle(SafeEventHandle safe_event_handle)
{
    if (safe_event_handle.IsInvalid)
    {
        throw new Exception("The handle is invalid.");
    }
    else
    {
        return new SafeWaitHandle(safe_event_handle.handle, false);
    }
}

Here, a SafeWaitHandle instance is created with the handle of a SafeEventHandle object and with ownership of handle specifically set to false and then returned.

7.3 With the SafeWaitHandle implicit conversion operator, we can perform the wait operation in the following manner :

private static void ExampleWaitEvent02()
{
    InitializeTimeConsumingProcess();

    try
    {
        // Obtain a handle to the Win32 Event Object named as "TIME_CONSUMER"
        // and wrap it inside a SafeEventHandle object.
        using (SafeEventHandle hEvent = OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, false, "TIME_CONSUMER"))
        {
            // Start the time consuming process.
            PerformTimeConsumingProcess();

            // Create a ManualResetEvent object which is initially not set.
            ManualResetEvent manual_reset_event = new ManualResetEvent(false);
            // Set the SafeWaitHandle property of the ManualResetEvent object
            // to a SafeWaitHandle object which is returned from an implicit
            // conversion from a SafeEventHandle object to a SafeWaitHandle
            // object.
            manual_reset_event.SafeWaitHandle = hEvent;

            // Start the waiting process.
            if (manual_reset_event.WaitOne())
            {
                // Rsset the event.
                manual_reset_event.Reset();
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

    UninitializeTimeConsumingProcess();
}

The implicit conversion from a SafeEventHandle to a SafeWaitHandle is performed in the following line :

manual_reset_event.SafeWaitHandle = hEvent;

8. In Conclusion.

8.1 And there we have it – a fully functional SafeHandle-derived class that provides safe handling of an OS event handle.

8.2 I hope the reader will use the code for SafeEventHandle to experiment with management of various handle types.

8.3 I also hope that the sections that touched on reference counting has been useful and has provided the reader with a good understanding of the nature of OS handles.

 

Advertisements

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Discussion

Trackbacks/Pingbacks

  1. Pingback: Example Custom Marshaler – The File Handle Marshaler | limbioliong - November 24, 2013

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: