Solved

Does JournalRecord hook from service require "Allow service to interact With Desktop"?

Posted on 2003-11-11
8
1,261 Views
Last Modified: 2012-05-04
Hello!
  I have restarted working on my work-monitoring utility :-)
  I was wprking with my Journal Record service and noticed that it worked only when I check the "Allow service to interact With Desktop" checkbox. If it is not checked, I get the message "No Journal Hook availible" in my file.
  The service monitors different mouse/keyboard events and logs them in a file "C:\IdlLog.Txt".
  I am running it under Win XP and have compiled it in Delphi 7.
  Can some body tell me why this could happen and what is the solution?
  Here's the unit:
unit WorkUnit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
  ExtCtrls;

Const
  TimeToSec = 86400;

type
  TService1 = class(TService)
    Timer1: TTimer;
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure ServiceExecute(Sender: TService);
    procedure Timer1Timer(Sender: TObject);

  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;

var
  Service1: TService1;
  MyHook, KHook: THandle;
  LastActivityTime, LastCheckTime, TheTotalIdleCount, TheCurrentIdleCount,
  TheIdleCutOff, TheCurrentWorkCount : TDateTime;
  FHookStarted : Boolean;
  MsgLog: TStringList;
  KbdLog: String;
implementation

{$R *.DFM}

Function SecondOfTheDay(CnvTime: TDateTime): Integer;
Begin
  Result := Round(CnvTime * TimeToSec);
End;

Function GetTimeDiff(CurTime, PreTime: TDateTime): TDateTime;
Begin


  If CurTime < PreTime Then
  Begin
    CurTime := 1 + CurTime;
  End;
  Result := CurTime - PreTime;

End;



Procedure UpdateIdleTime(KeyFlag:Boolean = True);
var
  TimeDiff, CurTime: TDateTime;

Begin

  CurTime := TIME;
  If KeyFlag Then
  Begin
    TimeDiff := GetTimeDiff(CurTime, LastActivityTime);
    If TimeDiff >= TheIdleCutOff Then
    Begin
      TheTotalIdleCount := TheTotalIdleCount + TimeDiff;
      TheCurrentIdleCount := TheCurrentIdleCount + (GetTimeDiff(CurTime, LastCheckTime));
    End
    Else
    Begin
      TheCurrentWorkCount := TheCurrentWorkCount + TimeDiff;
    end;

    LastCheckTime := CurTime;
    LastActivityTime := LastCheckTime;
  End
  Else
  Begin
    TimeDiff := GetTimeDiff(CurTime, LastCheckTime);
    If TimeDiff >= TheIdleCutOff Then
    Begin
      TheCurrentIdleCount := TheCurrentIdleCount + TimeDiff;
    End;
    LastCheckTime := CurTime;
  End;

End;



Function GetWorkStats: Word;

Begin

  Result := SecondOfTheDay(TheCurrentWorkCount);
  TheCurrentWorkCount := 0;

End;


procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

function JournalProc(Code, wParam: Integer; var EventStrut: TEventMsg): Integer; stdcall;
var
  s: string;
  KbdFlag: Boolean;
begin
  {this is the JournalRecordProc}
  Result := CallNextHookEx(MyHook, Code, wParam, Longint(@EventStrut));
  {the CallNextHookEX is not really needed for journal hook since it it not
  really in a hook chain, but it's standard for a Hook}
  if Code < 0 then Exit;

  {you should cancel operation if you get HC_SYSMODALON}
  if Code = HC_SYSMODALON then Exit;
  if Code = HC_ACTION then
  begin
    {
    The lParam parameter contains a pointer to a TEventMsg
    structure containing information on
    the message removed from the system message queue.
    }

    s := '';
    KbdFlag := False;
    if EventStrut.message = WM_LBUTTONUP then
      s := 'Left Mouse UP at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_RBUTTONUP) then
      s := 'Right Mouse Up at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_MOUSEWHEEL) then
      s := 'Mouse Wheel at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_KEYUP) then
    Begin
      KbdFlag := True;
      s := 'Keyboard';
      KbdLog := KbdLog + Format('<%d>',[EventStrut.paramL]);

      If wParam = VK_Return Then
      Begin
        KbdFlag := False;
        s := KbdLog;
        KbdLog := '';
      End;
    End;

    if s <> '' then
    Begin
      If not KbdFlag then
        MsgLog.Add(s);
      UpdateIdleTime;
    End;
  end;
end;

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
Var
  CutOffSec, CheckSec: Integer;
begin

  CutOffSec := 5;
  CheckSec := 10;
  MsgLog := TStringList.Create;
  KbdLog :='';
  if FHookStarted then
  begin
    MsgLog.Add('Mouse is already being Journaled, can not restart');
    Exit;
  end;

  LastActivityTime := TIME;
  LastCheckTime := LastActivityTime;
  TheIdleCutOff := CutOffSec / TimeToSec;
  TheTotalIdleCount := 0;
  TheCurrentIdleCount := 0;
  TheCurrentWorkCount := 0;

  MyHook := SetWindowsHookEx(WH_JOURNALRECORD, @JournalProc, hInstance, 0);
  Timer1.Interval := CheckSec * 1000;
  Timer1.Enabled := True;
  {SetWindowsHookEx starts the Hook}
  if MyHook > 0 then
  begin
    FHookStarted := True;
  end
  else
    MsgLog.Add('No Journal Hook availible');
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  FHookStarted := False;
  Timer1.Enabled := False;
  Timer1Timer(Timer1);
  UpdateIdleTime;
  UnhookWindowsHookEx(MyHook);
  MyHook := 0;
  MsgLog.Add(KbdLog);
  MsgLog.SaveToFile('C:\IdlLog.Txt');
  MsgLog.Free;
end;

procedure TService1.ServiceExecute(Sender: TService);
begin
 while not Terminated do
    ServiceThread.ProcessRequests(True);
end;

procedure TService1.Timer1Timer(Sender: TObject);
Var
  TotalIdl, CurIdl, CurWrk: Word;
begin
  CurWrk := GetWorkStats;
  MsgLog.Add(Format('Work = %d',[CurWrk]));
end;

end.

0
Comment
Question by:snehanshu
8 Comments
 
LVL 11

Assisted Solution

by:robert_marquardt
robert_marquardt earned 20 total points
ID: 9777128
Applying my common sense:
You want to access an application. An application runs on a desktop.
0
 
LVL 5

Author Comment

by:snehanshu
ID: 9777165
>>You want to access an application.
I thought I wanted to catch windows messages.
Does it mean that keyboard hooks also would not work if I haven't checked this thing? I haven't tried it, but want to know.
What classifies as "Desktop thing" and what does not?
...Snehanshu
0
 
LVL 6

Assisted Solution

by:pritaeas
pritaeas earned 50 total points
ID: 9778026
You may find some info here

http://www.geocities.com/macrotech_tr/API.html

hth, pritaeas
0
Back Up Your Microsoft Windows Server®

Back up all your Microsoft Windows Server – on-premises, in remote locations, in private and hybrid clouds. Your entire Windows Server will be backed up in one easy step with patented, block-level disk imaging. We achieve RTOs (recovery time objectives) as low as 15 seconds.

 
LVL 6

Assisted Solution

by:GloomyFriar
GloomyFriar earned 50 total points
ID: 9778365
When you have the "Allow service to interact With Desktop" checkbox unchecked your service run in a "black box" so it can't do what you want.
0
 
LVL 5

Author Comment

by:snehanshu
ID: 9778459
GloomyFriar,
>>your service run in a "black box"
  Is it true about JournalRecord or any hook?
  And is there some place where I can find information on what lies inside the black box and what lies outside?
:-)
Thanks,
...Snehanshu
0
 
LVL 12

Accepted Solution

by:
Lee_Nover earned 130 total points
ID: 9778955
imo mouse and kbd aren't accessible from a service
you can have different settings for them for each user
that's why you need to interact with the desktop

I also have a monitoring service and it needs to be interactive (I use the mouse and kbd hooks separately)

a simple rule of thumb .. anything that can be customized on a user level can't be accessed from a service unless it's interactive OR by using user Impersonation
0
 
LVL 5

Author Comment

by:snehanshu
ID: 9786853
Lee_Nover,
>>unless it's interactive
  I didn't know you could enable that check box on install just by changing the interactive property to true at design time!
  I have also got a few links from experts at Windows Programming TA and looks like I am all set to continue my task for now.
http://www.experts-exchange.com/Programming/Programming_Platforms/Win_Prog/Q_20802669.html
  Thank you all your help.
...Snehanshu
0
 
LVL 5

Author Comment

by:snehanshu
ID: 9786855
>>Thank you all your help.
Thank you all for your help.
:-)
0

Featured Post

ScreenConnect 6.0 Free Trial

Discover new time-saving features in one game-changing release, ScreenConnect 6.0, based on partner feedback. New features include a redesigned UI, app configurations and chat acknowledgement to improve customer engagement!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…

832 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question