(*
 * Author  : Michael Puff - http://www.michael-puff.de
 * Date    : 2006-03-15
 *)

(*======================================================================*
 |                                                                      |
 |                        COPYRIGHT NOTICE                              |
 |                                                                      |
 | Copyright (c) 2001-2006, Michael Puff ["copyright holder(s)"]        |
 | All rights reserved.                                                 |
 |                                                                      |
 | Redistribution and use in source and binary forms, with or without   |
 | modification, are permitted provided that the following conditions   |
 | are met:                                                             |
 |                                                                      |
 | 1. Redistributions of source code must retain the above copyright    |
 |    notice, this list of conditions and the following disclaimer.     |
 | 2. Redistributions in binary form must reproduce the above copyright |
 |    notice, this list of conditions and the following disclaimer in   |
 |    the documentation and/or other materials provided with the        |
 |    distribution.                                                     |
 | 3. The name(s) of the copyright holder(s) may not be used to endorse |
 |    or promote products derived from this software without specific   |
 |    prior written permission.                                         |
 |                                                                      |
 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
 | FORA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE        |
 | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;     |
 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER     |
 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT   |
 | LIABILITY, OR TORT INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY |
 | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
 | POSSIBILITY OF SUCH DAMAGE.                                          |
 |                                                                      |
 *======================================================================*)



unit MpuRegNotifierCls;

{.DEFINE UNICODE}

interface

uses
  { Delphi Win }
  Windows,
  { Delphi RTL }
  Classes;

type
{$IFDEF UNICODE}
  TextString = System.WideString;
  LPTSTR     = System.PWideChar;
{$ELSE !UNICODE}
  TextString = System.AnsiString;
  LPTSTR     = Windows.LPTSTR;
{$ENDIF UNICODE}

type
  TRegNotifier = class;

  TFNRegKeyChanged = procedure(ASender: TRegNotifier) of object;
  TFNRegKeyFailure = procedure(ASender: TRegNotifier; AErrCode: DWORD; const AErrText: TextString) of object;

  TRegNotifier = class(TThread)
  private
    FKey: HKEY;
    FSubKey: TextString;
    FErrCode: DWORD;
    FKeyHandle: HKEY;
    FEvtHandle: THandle;
    FOnRegKeyChanged: TFNRegKeyChanged;
    FOnRegKeyFailure: TFNRegKeyFailure;
  private
    procedure HandleError(AErrorCode: DWORD);
    procedure DoNotify();
    procedure DoError();
  protected
    procedure Execute(); override;
    procedure DoTerminate(); override;
  public
    constructor Create(AKey: HKEY; const ASubKey: TextString); virtual;
  public
    property Key: HKEY read FKey;
    property SubKey: TextString read FSubKey;
    property OnRegKeyChanged: TFNRegKeyChanged read FOnRegKeyChanged write FOnRegKeyChanged;
    property OnRegKeyFailure: TFNRegKeyFailure read FOnRegKeyFailure write FOnRegKeyFailure;
  end;

implementation

{ TRegNotfier }

constructor TRegNotifier.Create(AKey: HKEY; const ASubKey: TextString);
begin
  inherited Create(True);
  FreeOnTerminate := True;
  FKey := AKey;
  FSubKey := ASubKey;
  UniqueString(FSubKey);
end;

{*
 *  Procedure: TRegNotifier.HandleError
 *  Synchronizes the DoError Event  
 *  Author    : michael.puff
 *  Date      : 2006-03-15
 *}
procedure TRegNotifier.HandleError(AErrorCode: DWORD);
begin
  FErrCode := AErrorCode;
  Synchronize(DoError);
end;

{*
 *  Procedure: TRegNotifier.DoNotify
 *  Raises OnKeyChange Event
 *  Author    : michael.puff
 *  Date      : 2006-03-15
 *}
procedure TRegNotifier.DoNotify();
begin
  try
    if Assigned(FOnRegKeyChanged) then
      FOnRegKeyChanged(Self);
  except
    // ignore exceptions
  end;
end;

{*
 *  Procedure: TRegNotifier.DoError
 *  Raises OnKeyFailure Event
 *  Author    : michael.puff
 *  Date      : 2006-03-15
 *}
procedure TRegNotifier.DoError();
var
  ErrText: TextString;
begin
  try
    if Assigned(FOnRegKeyFailure) then
    begin
      // Get error text
      SetLength(ErrText, 1024);
      SetLength(ErrText, FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil,
        FErrCode, LANG_USER_DEFAULT, LPTSTR(ErrText), Length(ErrText), nil));
      FOnRegKeyFailure(Self, FErrCode, ErrText);
    end;
  except
    // ignore exceptions
  end;
end;

{*
 *  Procedure: TRegNotifier.Execute
 *  
 *  Author    : michael.puff
 *  Date      : 2006-03-15
 *}
procedure TRegNotifier.Execute();
var
  RegResult: Longint;
begin

  // Open key
  RegResult := RegOpenKeyEx(FKey, LPTSTR(FSubKey), 0, KEY_NOTIFY, FKeyHandle);
  if RegResult <> ERROR_SUCCESS then
  begin
    HandleError(RegResult);
    Exit;
  end;

  // Create event
  FEvtHandle := CreateEvent(nil, False, False, nil);
  if 0 = FEvtHandle then
  begin
    HandleError(GetLastError);
    Exit;
  end;

  while not Terminated do
  begin

    // Register notification
    RegResult := RegNotifyChangeKeyValue(FKeyHandle, True, REG_NOTIFY_CHANGE_LAST_SET, FEvtHandle, True);
    if RegResult <> ERROR_SUCCESS then
    begin
      HandleError(RegResult);
      Exit;
    end;

    // Wait for events
    while not Terminated do
    begin

      case WaitForSingleObject(FEvtHandle, INFINITE) of
        WAIT_TIMEOUT:
          // timeout, go ahead
          Continue;
        WAIT_OBJECT_0:
          begin
            Synchronize(DoNotify);
            // leave inner loop
            Break;
          end;
        WAIT_FAILED:
          begin
            // error, leave procedure
            HandleError(GetLastError());
            Exit;
          end;
      else
        HandleError(ERROR_INTERNAL_ERROR);
        Exit;
      end;

    end;
  end;
end;

{*
 *  Procedure: TRegNotifier.DoTerminate
 *  Clean up
 *  Author    : michael.puff
 *  Date      : 2006-03-15
 *}
procedure TRegNotifier.DoTerminate();
begin
  inherited DoTerminate();
  if FKeyHandle <> 0 then
    RegCloseKey(FKeyHandle);
  if FEvtHandle <> 0 then
    CloseHandle(FEvtHandle);
end;

end.


