Link to home
Start Free TrialLog in
Avatar of systan
systanFlag for Philippines

asked on

How to perfect this code

This is part of my personal project, I wanna perfect the code, but I can't.

I wanna open any executable file's,   but before launching the executable application,  I want my application to give the approval if the application is going to be launched or not.


Here's the complete code, and the attached file, and the image structure of what my application is doing.

//begin DPR
program Project1;
uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  FPipes in 'FPipes.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
//end DPR




//begin UNIT1
unit Unit1;
interface
uses Fpipes,
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;
type
  TForm1 = class(TForm)
    Button2: TButton;
    Button1: TButton;
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
     FServer: TPipeServer;
     procedure OnPipeData(Sender: TObject; aData: String; var aRespond: String);

  public
    { Public declarations }
    
  end;

var
  Form1: TForm1;
  //HookMsg: cardinal;

function SetHook(): Boolean; stdcall; external 'MatHook.dll';
function RemoveHook(): Boolean; stdcall; external 'MatHook.dll';

implementation

{$R *.dfm}

procedure TForm1.OnPipeData(Sender: TObject; aData: String; var aRespond: String);
begin
Form1.Text := aData;
//showmessage(aData);
//respond //test only
aRespond := '0';
end;


procedure TForm1.Button2Click(Sender: TObject);
begin
if (not RemoveHook) then ShowMessage('Couldn''t stop Hook');
FServer.Free;
Form1.Text := '';
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
if (not SetHook) then ShowMessage('Couldn''t start Hook');
FServer := TPipeServer.CreatePipeServer('', 'TwinSoft', True);
FServer.OnData := OnPipeData;
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if (not RemoveHook) then ShowMessage('Couldn''t stop Hook');
FServer.Free;
Form1.Text := '';
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if (not SetHook) then ShowMessage('Couldn''t start Hook');
FServer := TPipeServer.CreatePipeServer('', 'TwinSoft', True);
FServer.OnData := OnPipeData;
end;


//initialization
//HookMsg := RegisterWindowMessage('myhookdll_hookmsg');

end.
//end UNIT1





//begin unit FPIPES
unit FPipes;

interface

uses
  Classes, Windows;

const
  cShutDownMsg = 'shutdown';
  cPipeFormat = '\\%s\pipe\%s';

type
  RPIPEMessage = record
    Size: DWORD;
    Kind: Byte;
    Count: DWORD;
    Data: array[0..8095] of Char;
  end;

  TOnDataEvent = procedure(Sender: TObject; aData: String; var aRespond: String) of Object;

  TPipeServer = class(TThread)
  private
    FHandle: THandle;
    FPipeName: string;
    FOnData: TOnDataEvent;
  protected
  public
    constructor CreatePipeServer(aServer, aPipe: string; StartServer: Boolean);
    destructor Destroy; override;
    procedure StartUpServer;
    procedure ShutDownServer;
    procedure Execute; override;
    property OnData: TOnDataEvent read FOnData write FOnData;
  end;

  TPipeClient = class
  private
    FPipeName: string;
    function ProcessMsg(aMsg: RPIPEMessage): RPIPEMessage;
  protected
  public
    constructor Create(aServer, aPipe: string);
    function SendString(aStr: string): string;
  end;

implementation

uses
  SysUtils;

procedure CalcMsgSize(var Msg: RPIPEMessage);
begin
 Msg.Size := SizeOf(Msg.Size) +
             SizeOf(Msg.Kind) +
             SizeOf(Msg.Count) +
             Msg.Count + 3;
end;

{ TPipeServer }

constructor TPipeServer.CreatePipeServer(aServer, aPipe: string; StartServer: Boolean);
begin
 if aServer = '' then
  FPipeName := Format(cPipeFormat, ['.', aPipe])
 else
  FPipeName := Format(cPipeFormat, [aServer, aPipe]);
 FHandle := INVALID_HANDLE_VALUE;
 if StartServer then
  StartUpServer;
 Create(not StartServer);
end;

destructor TPipeServer.Destroy;
begin
 if FHandle <> INVALID_HANDLE_VALUE then
  ShutDownServer;
 inherited Destroy;
end;

procedure TPipeServer.Execute;
var
 i, w: Cardinal;
 InMsg,
 OutMsg: RPIPEMessage;
 s, r: String;
begin
 while not Terminated do
  begin
   if (FHandle = INVALID_HANDLE_VALUE) then
    Sleep(250)
   else
    if ConnectNamedPipe(FHandle, nil) then
     try
      InMsg.Size := SizeOf(InMsg);
      ReadFile(FHandle, InMsg, InMsg.Size, InMsg.Size, nil);
      if not Terminated and Assigned(FOnData) then
       begin
        s := '';
        for i := 0 to Pred(InMsg.Count) do
         s := s + InMsg.Data[i];
        FOnData(Self, s, r);
        OutMsg.Kind := 0;
        OutMsg.Count := Length(r);
        StrPCopy(OutMsg.Data, r);
        CalcMsgSize(OutMsg);
        WriteFile(FHandle, OutMsg, OutMsg.Size, w, nil);
       end;
     finally
      DisconnectNamedPipe(FHandle);
     end;
  end;
end;

procedure TPipeServer.ShutDownServer;
var
 InMsg,
 OutMsg: RPIPEMessage;
 BytesRead: Cardinal;
begin
 if FHandle <> INVALID_HANDLE_VALUE then
  begin
   Terminate;
   OutMsg.Size := SizeOf(OutMsg);
   with InMsg do
    begin
     Kind := 0;
     Count := Length(cShutDownMsg);
     StrPCopy(Data, cShutDownMsg);
    end;
   CalcMsgSize(InMsg);
   CallNamedPipe(PChar(FPipeName), @InMsg, InMsg.Size, @OutMsg, OutMsg.Size, BytesRead, 100);
   CloseHandle(FHandle);
   FHandle := INVALID_HANDLE_VALUE;
  end;
end;

procedure TPipeServer.StartUpServer;
begin
 if WaitNamedPipe(PChar(FPipeName), 100 {ms}) then
  raise Exception.Create('Requested PIPE exists already.');

 FHandle := CreateNamedPipe(PChar(FPipeName), PIPE_ACCESS_DUPLEX,
                            PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT,
                            PIPE_UNLIMITED_INSTANCES, SizeOf(RPIPEMessage), SizeOf(RPIPEMessage),
                            NMPWAIT_USE_DEFAULT_WAIT, nil);

 if FHandle = INVALID_HANDLE_VALUE then
  raise Exception.Create('Could not create PIPE.');
end;

{ TPipeClient }

constructor TPipeClient.Create(aServer, aPipe: string);
begin
 inherited Create;
 if aServer = '' then
  FPipeName := Format(cPipeFormat, ['.', aPipe])
 else
  FPipeName := Format(cPipeFormat, [aServer, aPipe]);
end;

function TPipeClient.ProcessMsg(aMsg: RPIPEMessage): RPIPEMessage;
begin
 CalcMsgSize(aMsg);
 Result.Size := SizeOf(Result);
 if WaitNamedPipe(PChar(FPipeName), 10) then
  if not CallNamedPipe(PChar(FPipeName), @aMsg, aMsg.Size, @Result, Result.Size, Result.Size, 500) then
   raise Exception.Create('PIPE did not respond.')
  else
 else
  raise Exception.Create('PIPE does not exist.');
end;

function TPipeClient.SendString(aStr: string): string;
var
 Msg: RPIPEMessage;
begin
 Msg.Kind := 1;
 Msg.Count := Length(aStr);
 StrPCopy(Msg.Data, aStr);
 Msg := ProcessMsg(Msg);
 Result := Copy(Msg.Data, 1, Msg.Count);
end;

end.
//end unit FPIPES




//begin DLL
library MatHook;

uses
  Windows,
  Messages,
  Fpipes;

type
  THookRec = record
    hMatHook: HHOOK;
    hMatWnd: HWND;
    oldProc: Integer;
  end;

var
  map: DWord;
  buf: ^THookRec;

// new window proc - runs in context of target process
function MatWndProc(Handle: hWnd; Msg: uInt; wp: wParam; lp: lParam): LongInt; stdcall;
var aResult:string;
begin
  try
    case Msg of
      WM_CREATE:
      begin
        //MessageBox(0, GetCommandLine, 'Command Line parameter(s)', MB_OK);

 with TPipeClient.Create('', 'TwinSoft') do
  try
   aResult := SendString(GetCommandLine);
  finally
   Free;
  end;


if aResult = '1' then
begin
Result := CallWindowProc(Pointer(buf^.oldProc), Handle, Msg, wp, lp);
MessageBox(0, GetCommandLine, 'THIS WILL CONTINUE LAUNCHING', MB_OK);
end
else
begin
result := 1;
MessageBox(0, GetCommandLine, 'THIS WILL NOT CONTINUE TO OPEN', MB_OK);
end;


      end;

      // user definied message to stop subclassing
      // (RegisterWindowMessage would be a better choice instead of WM_USER message!)
      WM_USER + 1:
      begin
         MessageBox(0, 'GetCommandLine', 'USER', MB_OK);
        // delete custom menu entries (quick'n'dirty)
        SetWindowLong(buf^.hMatWnd, GWL_WNDPROC, buf^.oldProc);
      end;

    end;

Result := CallWindowProc(Pointer(buf^.oldProc), Handle, Msg, wp, lp);



//Result := DefWindowProc(hWnd,Msg,wParam,lParam);


  except
    Result := 0;
  end;



//result :=  DefWindowProc(Handle,Msg,wP,lP);

end;

// hook proc - waits for target window to be created
function MatHookProc(nCode: Integer; wp: wParam; lp: lParam): LongInt; stdcall;
var
  hTemp: hWnd;
  szClass: array[0..255] of Char;
begin
  try
    if (nCode >= HC_ACTION) then
    begin
      Case nCode of
        HCBT_CREATEWND:
        begin



if EnumThreadWindows(GetCurrentThreadId, @hTemp, 0) then
begin
hTemp := HWND(wp);
FillChar(szClass, SizeOf(szClass), 0);
ZeroMemory(@szClass, SizeOf(szClass));
GetWindowText(hTemp, szClass, SizeOf(szClass));
GetClassName(hTemp, szClass, SizeOf(szClass));


            buf^.hMatWnd := htemp;
            buf^.oldProc := GetWindowLong(buf^.hMatWnd, GWL_WNDPROC);
            SetWindowLong(buf^.hMatWnd, GWL_WNDPROC, Integer(@MatWndProc));
end;

        end;
        HCBT_DESTROYWND:
        begin
           {//useless code
          hTemp := HWND(wp);
          FillChar(szClass, 256, 0);
          GetClassName(hTemp, szClass, 256);
          if (szClass = 'Notepad') then
          }



            SetWindowLong(buf^.hMatWnd, GWL_WNDPROC, buf^.OldProc);

        end;
      end;
    end;

    Result := CallNextHookEx(buf^.hMatHook, nCode, wp, lp);
  except
    Result := 0;
  end;
end;

// sets up hook
function SetHook: Boolean; stdcall; export;
begin
  try
    Result := false;
    if (not assigned(buf)) then
    begin
      map := CreateFileMapping(DWord(-1), nil, PAGE_READWRITE, 0, SizeOf(THookRec), 'HookRecMemBlock');
      buf := MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, 0);
      buf^.hMatHook := SetWindowsHookEx(WH_CBT, @MatHookProc, hInstance, 0);
      Result := true;
    end;
  except
    Result := false;
  end;
end;

// removes hook
function RemoveHook: Boolean; stdcall; export;
begin
  Result := false;
  if (assigned(buf)) then
  begin
    // tell our new wnd proc to stop subclassing
    // (has to be done in context of target process)
    SendMessage(buf^.hMatWnd, wm_User + 1, 1, 0);
    if (buf^.hMatHook <> 0) then UnhookWindowsHookEx(buf^.hMatHook);
    buf^.hMatHook := 0;
    UnmapViewOfFile(buf);
    buf := nil;
    Result := true;
  end;
end;

// DLL entry point
procedure DllEntry(dwReason: DWord);
begin
  Case dwReason of
    DLL_PROCESS_ATTACH:
    begin
      if (not assigned(buf)) then
      begin
        map := OpenFileMapping(FILE_MAP_ALL_ACCESS, false, 'HookRecMemBlock');
        buf := MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, 0);
        CloseHandle(map);
        map := 0;
      end;
    end;
    DLL_PROCESS_DETACH:
    begin
      UnmapViewOfFile(buf);
      buf := nil;
    end;
  end;
end;

exports
  SetHook,
  RemoveHook;

// main
begin
  DisableThreadLibraryCalls(hInstance);
  DllProc := @DLLEntry;
  DllEntry(DLL_PROCESS_ATTACH);
end.
//end DLL

Open in new window

Avatar of systan
systan
Flag of Philippines image

ASKER

the attached file
file.zip
Avatar of systan

ASKER

the image structure of what my application is doing.
eqimage2.JPG
Avatar of Geert G
perfect code ?
* by renaming units/projects/components with a relevant name
* indenting code
* adding comments to the code
* don't use form1 inside TForm1 (alternative = self.)

 
Avatar of abilash94
abilash94

guess it is correct!
Avatar of systan

ASKER

Geert;
I'm not sure about the point of your comment, I doubt.

I'm just asking if someone could check the code or edit the code to make it run better.

I've test it, and it run's Ok,  but when I open other some applications, it will hang my application.

eq.;
calc.exe - when openning this, no problem
dotnetapp.exe - when openning it delays to open
somedelphiapp - when opened is Ok
some other application's cause error, explorer error.
Avatar of systan

ASKER

@abilash94;
Welcome to experts-exchange, your e-e birth is today.
ow sorry, i thought you wanted recommendations about the code formatting too

i would change the use of Form1

procedure TForm1.OnPipeData(Sender: TObject; aData: String; var aRespond: String);
begin
  Form1.Text := aData;


>>change to
procedure TForm1.OnPipeData(Sender: TObject; aData: String; var aRespond: String);
begin
  Self.Text := aData;

it will avoid any needless actions when renaming TForm1 to TfrmAppStartResponse

I would change some things (like (very meaningfull names)
figuring out button2 is for stop and button1 is for start, face it, it's just wasting time isn't it ?
you are programming in delphi which is top-down logic
and you aren't following that all the way

type
  TForm1 = class(TForm)
  ...
  private
    procedure OnPipeData(Sender: TObject; aData: String; var aRespond: String);
  protected
    procedure StartPipe; virtual;
    procedure StopPipe; virtual;
  end;


procedure TForm1.OnPipeData(Sender: TObject; aData: String; var aRespond: String);
begin
  Self.Text := Copy(aData, 1, 100); // don't let it get too long
  //showmessage(aData);
  //respond //test only
  aRespond := '0';
end;


procedure TForm1.btnStopPipeClick(Sender: TObject);
begin
  StopPipe;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  StartPipe;
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  StopPipe;
  CanClose := True;
end;

procedure TForm1.btnStartPipe(Sender: TObject);
begin
  StartPipe;
end;

procedure TForm1.StartPipe;
begin
  if not Assigned(fServer) then
  begin
    if  SetHook then
    begin
      FServer := TPipeServer.CreatePipeServer('', 'TwinSoft', True);
      FServer.OnData := OnPipeData;
    end;
  end;
end;

procedure TForm1.StopPipe;
begin
  if Assigned(fServer) then
    RemoveHook;
  FreeAndNil(fServer);
end;

ASKER CERTIFIED SOLUTION
Avatar of Geert G
Geert G
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of systan

ASKER

Geert;
I will gladly change some of code by your recommendations on FPipes and to the Unit1 Pas.

But the realy problem of the code is when I openned an application. (other application)

Like openning an application from any .net compiled application.
Like openning My Documents, My Computer, it errors and hangs up the computer in a minute.
Like openning some applications and delays long or does not continue to open.

That's the hardiest part.
I think the problem is on the DLL part.
Here's the part of the code snippet that actually do the approval if the application will be openned or not;
// new window proc - runs in context of target process
function MatWndProc(Handle: hWnd; Msg: uInt; wp: wParam; lp: lParam): LongInt; stdcall;
var aResult:string;
begin
  try
    case Msg of
      WM_CREATE:
      begin
        //MessageBox(0, GetCommandLine, 'Command Line parameter(s)', MB_OK);

 with TPipeClient.Create('', 'TwinSoft') do
  try
   aResult := SendString(GetCommandLine);
  finally
   Free;
  end;

//I THINK the problem is here or down or the whole

if aResult = '1' then
begin
Result := CallWindowProc(Pointer(buf^.oldProc), Handle, Msg, wp, lp);
MessageBox(0, GetCommandLine, 'THIS WILL CONTINUE LAUNCHING', MB_OK);
end
else
begin
result := 1;
MessageBox(0, GetCommandLine, 'THIS WILL NOT CONTINUE TO OPEN', MB_OK);
end;


      end;

      // user definied message to stop subclassing
      // (RegisterWindowMessage would be a better choice instead of WM_USER message!)
      WM_USER + 1:
      begin
         MessageBox(0, 'GetCommandLine', 'USER', MB_OK);
        // delete custom menu entries (quick'n'dirty)
        SetWindowLong(buf^.hMatWnd, GWL_WNDPROC, buf^.oldProc);
      end;

    end;

Result := CallWindowProc(Pointer(buf^.oldProc), Handle, Msg, wp, lp);



//Result := DefWindowProc(hWnd,Msg,wParam,lParam);


  except
    Result := 0;
  end;



//result :=  DefWindowProc(Handle,Msg,wP,lP);

end;

Open in new window

Avatar of systan

ASKER

It's sad to say that I asked this question many times here in experts-exchange.

https://www.experts-exchange.com/questions/25616319/For-Mahdi78-and-Twinsoft-Changing-messages-from-delphi-DLL-to-delphi-Form-Vice-Versa.html
https://www.experts-exchange.com/questions/20798061/Detecting-application-launches.html

But, no one could perfect the code, and I can't, it's too hard to think whats the next line  to write.

Anyway, I have to wait for the code saviour,  if there is?  there is one,  but I don't know why didn't comment.

Hmp, all experts-exchange active member are code saviour's, sharing idea's and accept's a warm points,  infact even it's midnight to dawn they are not tired to help.
?midnight to dawn?
depends where you sit, remember ...site is worldwide
your midnight, could be my afternoon

have you tried with a fixed yes or no
--> no user interaction with messageboxes

just a plain if notepad.exe then 0 else 1

test that logic first with .net

if that don't work, you may have to post in matthias's forum to get help


Avatar of systan

ASKER

Thank you for the reply;
Yes, site is worldwide

I've tried removing the messageboxes.

just a plain notepad.exe is Ok to run, but others don't
In .Net applications, yes it run's but some other .net apps also does not show.

Oh, test in .net, I'm not sure with the code

Ok, where is matthia's forum? can you post the link, please.
just look on madshi's profile
currently overal top N°7 in Delphi
Avatar of systan

ASKER

Thanks, I'll try to look for that;
I'm wodering if "WaitForSingleObject" can be applied for that application
Avatar of systan

ASKER

I Think no one is listening aside from Geert;
Avatar of systan

ASKER

I don't believe this; all are listening...
Avatar of systan

ASKER

I guess I have to open this question again soon

Thanks Geert; for some tweaks