unit uEventHooks;

interface

uses SysUtils, Classes, Windows, Dialogs, Forms, ComObj, ActiveX,
     CPRSChart_TLB, ORNet, ORFn, uCore;

type
  TCPRSExtensionData = record
    Data1: string;
    Data2: string;
  end;

procedure RegisterCPRSTypeLibrary;
procedure ProcessPatientChangeEventHook;
function ProcessOrderAcceptEventHook(OrderID: string; DisplayGroup: integer): boolean;
procedure GetCOMObjectText(COMObject: integer; const Param2, Param3: string;
                           var Data1, Data2: string);
function COMObjectOK(COMObject: integer): boolean;
function COMObjectActive: boolean;

implementation

uses
  Trpcb, rEventHooks;

type
  ICPRSBrokerInitializer = interface(ICPRSBroker)
    procedure Initialize;
  end;

  TCPRSBroker = class(TAutoIntfObject, ICPRSBrokerInitializer)
  private
    FContext: string;
    FRPCVersion: string;
    FClearParameters: boolean;
    FClearResults: boolean;
    FResults: string;
    FParam: TParams;
    FEmptyParams: TParams;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Initialize;
    function  SetContext(const Context: WideString): WordBool; safecall;
    function  Server: WideString; safecall;
    function  Port: Integer; safecall;
    function  DebugMode: WordBool; safecall;
    function  Get_RPCVersion: WideString; safecall;
    procedure Set_RPCVersion(const Value: WideString); safecall;
    function  Get_ClearParameters: WordBool; safecall;
    procedure Set_ClearParameters(Value: WordBool); safecall;
    function  Get_ClearResults: WordBool; safecall;
    procedure Set_ClearResults(Value: WordBool); safecall;
    procedure CallRPC(const RPCName: WideString); safecall;
    function  Get_Results: WideString; safecall;
    procedure Set_Results(const Value: WideString); safecall;
    function  Get_Param(Index: Integer): WideString; safecall;
    procedure Set_Param(Index: Integer; const Value: WideString); safecall;
    function  Get_ParamType(Index: Integer): BrokerParamType; safecall;
    procedure Set_ParamType(Index: Integer; Value: BrokerParamType); safecall;
    function  Get_ParamList(Index: Integer; const Node: WideString): WideString; safecall;
    procedure Set_ParamList(Index: Integer; const Node: WideString; const Value: WideString); safecall;
    function  ParamCount: Integer; safecall;
    function  ParamListCount(Index: Integer): Integer; safecall;
    property RPCVersion: WideString read Get_RPCVersion write Set_RPCVersion;
    property ClearParameters: WordBool read Get_ClearParameters write Set_ClearParameters;
    property ClearResults: WordBool read Get_ClearResults write Set_ClearResults;
    property Results: WideString read Get_Results write Set_Results;
    property Param[Index: Integer]: WideString read Get_Param write Set_Param;
    property ParamType[Index: Integer]: BrokerParamType read Get_ParamType write Set_ParamType;
    property ParamList[Index: Integer; const Node: WideString]: WideString read Get_ParamList write Set_ParamList;
  end;

  TCPRSState = class(TAutoIntfObject, ICPRSState)
  private
    FHandle: string;
  public
    constructor Create;
    function  Handle: WideString; safecall;
    function  UserDUZ: WideString; safecall;
    function  UserName: WideString; safecall;
    function  PatientDFN: WideString; safecall;
    function  PatientName: WideString; safecall;
    function  PatientDOB: WideString; safecall;
    function  PatientSSN: WideString; safecall;
    function  LocationIEN: Integer; safecall;
    function  LocationName: WideString; safecall;
  end;

  TCPRSEventHookManager = class(TObject)
  private
    FCPRSBroker: ICPRSBrokerInitializer;
    FCPRSState: ICPRSState;
    FErrors: TStringList;
    FLock: TRTLCriticalSection;
  public
    constructor Create;
    destructor Destroy; override;
    function ProcessComObject(const GUIDString: string;
                              const AParam2, AParam3: string;
                               var Data1, Data2: WideString): boolean;
    procedure EnterCriticalSection;
    procedure LeaveCriticalSection;
  end;

  
var
  uCPRSEventHookManager: TCPRSEventHookManager = nil;
  uCOMObjectActive: boolean = False;

procedure EnsureEventHookObjects;
begin
  if not assigned(uCPRSEventHookManager) then
    uCPRSEventHookManager := TCPRSEventHookManager.Create;
end;

{ TCPRSBroker }

constructor TCPRSBroker.Create;
var
  CPRSLib: ITypeLib;

begin
  FParam := TParams.Create(nil);
  FEmptyParams := TParams.Create(nil);
  OleCheck(LoadRegTypeLib(LIBID_CPRSChart, 1, 0, 0, CPRSLib));
  inherited Create(CPRSLib, ICPRSBroker);
  EnsureBroker;
end;

procedure TCPRSBroker.CallRPC(const RPCName: WideString);
var
  err: boolean;
  tmpRPCVersion: string;
  tmpClearParameters: boolean;
  tmpClearResults: boolean;
  tmpResults: string;
  tmpParam: TParams;

begin
  EnsureEventHookObjects;
  uCPRSEventHookManager.EnterCriticalSection;
  try
    err := (FContext = '');
    if(not err) then
      err := not UpdateContext(FContext);
    if (not err) then
      err := IsBaseContext;
    if err then
      raise EOleException.Create('Invalid Broker Context', OLE_E_FIRST, Application.ExeName ,'', 0)
    else
    begin
      if RPCName <> '' then
      begin
        tmpRPCVersion := RPCBrokerV.RpcVersion;
        tmpClearParameters := RPCBrokerV.ClearParameters;
        tmpClearResults := RPCBrokerV.ClearResults;
        tmpResults := RPCBrokerV.Results.Text;
        tmpParam := TParams.Create(nil);
        try
          RPCBrokerV.RemoteProcedure := RPCName;
          RPCBrokerV.RpcVersion := FRPCVersion;
          RPCBrokerV.ClearParameters := FClearParameters;
          RPCBrokerV.ClearResults := FClearResults;
          RPCBrokerV.Param.Assign(FParam);
          CallBrokerInContext;
          FParam.Assign(RPCBrokerV.Param);
          FResults := RPCBrokerV.Results.Text;
        finally
          RPCBrokerV.RpcVersion := tmpRPCVersion;
          RPCBrokerV.ClearParameters := tmpClearParameters;
          RPCBrokerV.ClearResults := tmpClearResults;
          RPCBrokerV.Results.Text := tmpResults;
          RPCBrokerV.Param.Assign(tmpParam);
          tmpParam.Free;
        end;
      end
      else
      begin
        RPCBrokerV.Results.Clear;
        FResults := '';
      end;
    end;
  finally
    uCPRSEventHookManager.LeaveCriticalSection;
  end;
end;

function TCPRSBroker.DebugMode: WordBool;
begin
  Result := RPCBrokerV.DebugMode;
end;

function TCPRSBroker.Get_ClearParameters: WordBool;
begin
  Result := FClearParameters;
end;

function TCPRSBroker.Get_ClearResults: WordBool;
begin
  Result := FClearResults;
end;

function TCPRSBroker.Get_Param(Index: Integer): WideString;
begin
  Result := FParam[Index].Value;
end;

function TCPRSBroker.Get_ParamList(Index: Integer;
  const Node: WideString): WideString;
begin
  Result := FParam[Index].Mult[Node];
end;

function TCPRSBroker.Get_ParamType(Index: Integer): BrokerParamType;
begin
  case FParam[Index].PType of
    literal:   Result := bptLiteral;
    reference: Result := bptReference;
    list:      Result := bptList;
    else       Result := bptUndefined;
  end;
end;

function TCPRSBroker.Get_Results: WideString;
begin
  Result := FResults;
end;

function TCPRSBroker.Get_RPCVersion: WideString;
begin
  Result := FRPCVersion;
end;

function TCPRSBroker.ParamCount: Integer;
begin
  Result := FParam.Count;
end;

function TCPRSBroker.ParamListCount(Index: Integer): Integer;
begin
  Result := FParam[Index].Mult.Count;
end;

function TCPRSBroker.Port: Integer;
begin
  Result := RPCBrokerV.ListenerPort;
end;

function TCPRSBroker.Server: WideString;
begin
  Result := RPCBrokerV.Server;
end;

procedure TCPRSBroker.Set_ClearParameters(Value: WordBool);
begin
  FClearParameters := Value;
end;

procedure TCPRSBroker.Set_ClearResults(Value: WordBool);
begin
  FClearResults := Value;
end;

procedure TCPRSBroker.Set_Param(Index: Integer; const Value: WideString);
begin
  FParam[Index].Value := Value;
end;

procedure TCPRSBroker.Set_ParamList(Index: Integer; const Node,
  Value: WideString);
begin
  FParam[Index].Mult[Node] := Value;
end;

procedure TCPRSBroker.Set_ParamType(Index: Integer;
  Value: BrokerParamType);
begin
  case Value of
    bptLiteral:   FParam[Index].PType := literal;
    bptReference: FParam[Index].PType := reference;
    bptList:      FParam[Index].PType := list;
    else          FParam[Index].PType := undefined;
  end;
end;

procedure TCPRSBroker.Set_Results(const Value: WideString);
begin
  FResults := Value;
end;

procedure TCPRSBroker.Set_RPCVersion(const Value: WideString);
begin
  FRPCVersion := Value;
end;

function TCPRSBroker.SetContext(const Context: WideString): WordBool;
begin
  FContext := Context;
  Result := UpdateContext(FContext);
end;

procedure TCPRSBroker.Initialize;
begin
  FContext := '';
  FRPCVersion := RPCBrokerV.RpcVersion;
  FClearParameters := RPCBrokerV.ClearParameters;
  FClearResults := RPCBrokerV.ClearResults;
  FResults := '';
  FParam.Assign(FEmptyParams);
end;

destructor TCPRSBroker.Destroy;
begin
  FParam.Free;
  FEmptyParams.Free;
  inherited;
end;

{ TCPRSState }

constructor TCPRSState.Create;
var
  CPRSLib: ITypeLib;

begin
  OleCheck(LoadRegTypeLib(LIBID_CPRSChart, 1, 0, 0, CPRSLib));
  inherited Create(CPRSLib, ICPRSState);
  FHandle := DottedIPStr + 'x' + IntToHex(Application.Handle,8);
end;

function TCPRSState.Handle: WideString;
begin
  Result := FHandle;
end;

function TCPRSState.LocationIEN: Integer;
begin
  Result := Encounter.Location;
end;

function TCPRSState.LocationName: WideString;
begin
  Result := Encounter.LocationName;
end;

function TCPRSState.PatientDFN: WideString;
begin
  Result := Patient.DFN;
end;

function TCPRSState.PatientDOB: WideString;
begin
  Result := FormatFMDateTime('mm/dd/yyyy', Patient.DOB);
end;

function TCPRSState.PatientName: WideString;
begin
  Result := Patient.Name;
end;

function TCPRSState.PatientSSN: WideString;
begin
  Result := Patient.SSN;
end;

function TCPRSState.UserDUZ: WideString;
begin
  Result := IntToStr(User.DUZ);
end;

function TCPRSState.UserName: WideString;
begin
  Result := User.Name;
end;

{ TCPRSEventHookManager }

constructor TCPRSEventHookManager.Create;
begin
  inherited;
  FCPRSBroker := TCPRSBroker.Create;
  FCPRSState := TCPRSState.Create;
end;

destructor TCPRSEventHookManager.Destroy;
begin
  FCPRSState := nil;
  FCPRSBroker := nil;
  if assigned(FErrors) then
    FErrors.Free;
  inherited;
end;

procedure TCPRSEventHookManager.EnterCriticalSection;
begin
  Windows.EnterCriticalSection(FLock);
end;

procedure TCPRSEventHookManager.LeaveCriticalSection;
begin
  Windows.LeaveCriticalSection(FLock);
end;

function TCPRSEventHookManager.ProcessComObject(const GUIDString: string;
                               const AParam2, AParam3: string;
                               var Data1, Data2: WideString): boolean;
var
  ObjIEN, ObjName, ObjGUIDStr, err, AParam1: string;
  ObjGUID: TGUID;
  ObjIntf: IUnknown;
  Obj: ICPRSExtension;

begin
  Result := FALSE;
  ObjIEN := Piece(GUIDString,U,1);
  if assigned(FErrors) and (FErrors.IndexOf(ObjIEN) >= 0) then exit;
  ObjName := Piece(GUIDString,U,2);
  ObjGUIDStr := Piece(GUIDString,U,3);
  if (ObjGUIDStr <> '') then
  begin
    try
      ObjGUID := StringToGUID(ObjGUIDStr);
      try
        ObjIntf := CreateComObject(ObjGUID);
        if assigned(ObjIntf) then
        begin
          try
            ObjIntf.QueryInterface(IID_ICPRSExtension, Obj);
            if assigned(Obj) then
            begin
              AParam1 := Piece(GUIDString,U,5);
              InitializeCriticalSection(FLock);
              try
                FCPRSBroker.Initialize;
                uCOMObjectActive := True;
                Result := Obj.Execute(FCPRSBroker, FCPRSState,
                                      AParam1, AParam2, AParam3, Data1, Data2);
              finally
                DeleteCriticalSection(FLock);
                uCOMObjectActive := False;
              end;
            end
            else
              err := 'COM Object ' + ObjName + ' does not support ICPRSExtension';
          except
            err := 'Error executing ' + ObjName;
          end;
        end;
      except
        err := 'COM Object ' + ObjName + ' not found on this workstation.';
      end;
    except
      err := 'COM Object ' + ObjName + ' has an invalid GUID' + CRLF + ObjGUIDStr;
    end;
    if err <> '' then
    begin
      if not assigned(FErrors) then
        FErrors := TStringList.Create;
      if FErrors.IndexOf(ObjIEN) < 0 then
        FErrors.Add(ObjIEN);
      ShowMessage(err);
    end;
  end;
end;

procedure FreeEventHookObjects;
begin
  FreeAndNil(uCPRSEventHookManager);
end;

// External Calls

procedure RegisterCPRSTypeLibrary;
type
  TUnregisterProc = function(const GUID: TGUID; VerMajor, VerMinor: Word;
    LCID: TLCID; SysKind: TSysKind): HResult stdcall;

var
  Unregister: boolean;
  CPRSLib: ITypeLib;
  DoHalt: boolean;
  ModuleName: string;
  HelpPath: WideString;
  Buffer: array[0..261] of Char;
  Handle: THandle;
  UnregisterProc: TUnregisterProc;
  LibAttr: PTLibAttr;

begin
  DoHalt := TRUE;
  if FindCmdLineSwitch('UNREGSERVER', ['-', '/'], True) then
    Unregister := TRUE
  else
  begin
    Unregister := FALSE;
    if not FindCmdLineSwitch('REGSERVER', ['-', '/'], True) then
      DoHalt := FALSE;
  end;

  try
    SetString(ModuleName, Buffer, Windows.GetModuleFileName(HInstance, Buffer, SizeOf(Buffer)));
    if ModuleName <> '' then
    begin
      OleCheck(LoadTypeLib(PWideChar(WideString(ModuleName)), CPRSLib)); // will register if needed
      if assigned(CPRSLib) then
      begin
        if Unregister then
        begin
          Handle := GetModuleHandle('OLEAUT32.DLL');
          if Handle <> 0 then
          begin
            @UnregisterProc := GetProcAddress(Handle, 'UnRegisterTypeLib');
            if @UnregisterProc <> nil then
            begin
              OleCheck(CPRSLib.GetLibAttr(LibAttr));
              try
                with LibAttr^ do
                  UnregisterProc(guid, wMajorVerNum, wMinorVerNum, lcid, syskind);
              finally
                CPRSLib.ReleaseTLibAttr(LibAttr);
              end;
            end;
          end;
        end
        else
        begin
          HelpPath := ExtractFilePath(ModuleName);
          OleCheck(RegisterTypeLib(CPRSLib, PWideChar(WideString(ModuleName)), PWideChar(HelpPath)));
        end;
      end;
    end;
  except
// ignore any errors   
  end;
  if DoHalt then Halt;
end;

procedure ProcessPatientChangeEventHook;
var
  d1, d2: WideString;
  COMObj: string;

begin
  COMObj := GetPatientChangeGUIDs;
  if(COMObj <> '') and (COMObj <> '0') then
  begin
    EnsureEventHookObjects;
    d1 := '';
    d2 := '';
    uCPRSEventHookManager.ProcessComObject(COMObj, 'P=' + Patient.DFN, '', d1, d2);
  end;
end;

function ProcessOrderAcceptEventHook(OrderID: string; DisplayGroup: integer): boolean;
var
  d1, d2: WideString;
  COMObj: string;

begin
  Result := False;
  COMObj := GetOrderAcceptGUIDs(DisplayGroup);
  if(COMObj <> '') and (COMObj <> '0') then
  begin
    EnsureEventHookObjects;
    d1 := '';
    d2 := '';
    //Result will be set to True by Com object if the order is deleted by LES
    Result := uCPRSEventHookManager.ProcessComObject(COMObj, 'O=' + OrderID, '', d1, d2);
  end;
end;

procedure GetCOMObjectText(COMObject: integer; const Param2, Param3: string;
                           var Data1, Data2: string);
var
  d1, d2: WideString;
  COMObj: string;

begin
  if COMObject > 0 then
  begin
    COMObj := GetCOMObjectDetails(COMObject);
    if(COMObj <> '') and (COMObj <> '0') then
    begin
      EnsureEventHookObjects;
      d1 := Data1;
      d2 := Data2;
      if uCPRSEventHookManager.ProcessComObject(COMObj, Param2, Param3, d1, d2) then
      begin
        Data1 := d1;
        Data2 := d2;
      end;
    end;
  end;
end;

function COMObjectOK(COMObject: integer): boolean;
begin
  if assigned(uCPRSEventHookManager) and assigned(uCPRSEventHookManager.FErrors) then
    Result := (uCPRSEventHookManager.FErrors.IndexOf(IntToStr(COMObject)) < 0)
  else
    Result := TRUE;
end;

function COMObjectActive: boolean;
begin
  Result := uCOMObjectActive;
end;

initialization

finalization
  FreeEventHookObjects;

end.
