Following is a stripped-down version of c# code, which helps to capture the PrintScreen key. This work as I expected and tested.
Question: I am aware of deterministic destruction/disposal pattern, which I started to draft as below. However, I need some expert advice to complete my dispose and finalize method. Any advise ?
public class RegisterPrintKey : IDisposable
{
public delegate void HotKeyPass();
public event HotKeyPass HotKey;
private IntPtr m_WindowHandle = IntPtr.Zero;
private MKEY m_ModKey = MKEY.MOD_CONTROL;
private Keys m_Keys = Keys.A;
private HotKeyWndProc m_HotKeyWnd = new HotKeyWndProc();
[DllImport("user32.dll")]
public static extern bool RegisterHotKey(IntPtr wnd, int id, MKEY mode, Keys vk);
[DllImport("user32.dll")]
public static extern bool UnregisterHotKey(IntPtr wnd, int id);
private class HotKeyWndProc : NativeWindow
{
public int m_WParam = 10000;
public HotKeyPass m_HotKeyPass;
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0312 && m.WParam.ToInt32() == m_WParam)
{
if (m_HotKeyPass != null) m_HotKeyPass.Invoke();
}
base.WndProc(ref m);
}
}
private bool hasDisposed = false;
protected virtual void Dispose(bool dispose)
{
if (hasDisposed) return;
if (dispose)
{
//release objects owned by this instance
HotKey = null;
hasDisposed=true;
}
m_WindowHandle = IntPtr.Zero; // I presume this is not required.
m_Keys = null; //Do i need to dispose this or relay on stack ( clean up when thread unwind its stack)
m_ModKey = null;
m_HotKeyWnd.DestroyHandle();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~RegisterPrintKey()
{
Dispose(false);
}
}
public enum MKEY
{
MOD_ALT = 0x0001,
MOD_CONTROL = 0x0002,
MOD_SHIFT = 0x0004,
MOD_WIN = 0x0008,
}
Some suggestions on your code
public class RegisterPrintKey : IDisposable {
...
// This class can allocate the Window Handle resource (HWND)
private class HotKeyWndProc : NativeWindow {
}
// Explicit resource (HWND) allocation
private HotKeyWndProc m_HotKeyWnd = new HotKeyWndProc();
// I'd rather make a property from your "hasDisposed" field:
// - it's make easier to check instance's state (esp. while debugging)
// - IsDisposed is more popular name for this
public Boolean IsDisposed {
get;
protected set; // <- or even "private set"
}
protected virtual void Dispose(Boolean dispose) {
if (IsDisposed)
return;
if (disposed) {
// Release any Objects here
// You've allocated the HWND resource so you have to dispose it:
m_HotKeyWnd.DestroyHandle(); // <- Free HWND you've created
}
// Here, you may work with structures only!
// You don't need these assignments, but you can safely do them:
// mayhaps, they'll be useful for you while debugging
m_WindowHandle = IntPtr.Zero;
m_Keys = null;
m_ModKey = null;
// Finally, we've done with disposing the instance
IsDisposed = true;
}
public void Dispose() {
Dispose(true);
// We've done with Dispose: "GC, please, don't bother the disposed instance"
GC.SuppressFinalize(this);
}
// A treacherous enemy I've commented out:
// if you've made a error in Dispose() it'll be resource leak
// or something like AccessViolation. The only good thing is that
// you can easily reproduce (and fix) the problem.
// If you've uncommented ~RegisterPrintKey() this leak/EAV will become
// floating error: it'll appear and disappear on different workstations
// OSs etc: you can't control GC when to run. Floating error is
// much harder to detect.
// Another bad issue with finalizer is that it prevents the instance
// from being in zero generation, see
// http://stackoverflow.com/questions/12991692/why-doesnt-object-with-finalizer-get-collected-even-if-it-is-unrooted
//~RegisterPrintKey() {
// // This code can be called (if called!) at random time
// Dispose(false); // <- That's an enemy!
// }
}