snehanshu
asked on
Does JournalRecord hook from service require "Allow service to interact With Desktop"?
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:Boo lean = 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(TheCurrentW orkCount);
TheCurrentWorkCount := 0;
End;
procedure ServiceController(CtrlCode : DWord); stdcall;
begin
Service1.Controller(CtrlCo de);
end;
function TService1.GetServiceContro ller: 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(Sen der: 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_JOURNA LRECORD, @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(Send er: TService; var Stopped: Boolean);
begin
FHookStarted := False;
Timer1.Enabled := False;
Timer1Timer(Timer1);
UpdateIdleTime;
UnhookWindowsHookEx(MyHook );
MyHook := 0;
MsgLog.Add(KbdLog);
MsgLog.SaveToFile('C:\IdlL og.Txt');
MsgLog.Free;
end;
procedure TService1.ServiceExecute(S ender: TService);
begin
while not Terminated do
ServiceThread.ProcessReque sts(True);
end;
procedure TService1.Timer1Timer(Send er: TObject);
Var
TotalIdl, CurIdl, CurWrk: Word;
begin
CurWrk := GetWorkStats;
MsgLog.Add(Format('Work = %d',[CurWrk]));
end;
end.
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:Boo
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(TheCurrentW
TheCurrentWorkCount := 0;
End;
procedure ServiceController(CtrlCode
begin
Service1.Controller(CtrlCo
end;
function TService1.GetServiceContro
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
if (EventStrut.message = WM_RBUTTONUP) then
s := 'Right Mouse Up at X pos ' +
IntToStr(EventStrut.paramL
if (EventStrut.message = WM_MOUSEWHEEL) then
s := 'Mouse Wheel at X pos ' +
IntToStr(EventStrut.paramL
if (EventStrut.message = WM_KEYUP) then
Begin
KbdFlag := True;
s := 'Keyboard';
KbdLog := KbdLog + Format('<%d>',[EventStrut.
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(Sen
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_JOURNA
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(Send
begin
FHookStarted := False;
Timer1.Enabled := False;
Timer1Timer(Timer1);
UpdateIdleTime;
UnhookWindowsHookEx(MyHook
MyHook := 0;
MsgLog.Add(KbdLog);
MsgLog.SaveToFile('C:\IdlL
MsgLog.Free;
end;
procedure TService1.ServiceExecute(S
begin
while not Terminated do
ServiceThread.ProcessReque
end;
procedure TService1.Timer1Timer(Send
Var
TotalIdl, CurIdl, CurWrk: Word;
begin
CurWrk := GetWorkStats;
MsgLog.Add(Format('Work = %d',[CurWrk]));
end;
end.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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
>>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
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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.
https://www.experts-exchange.com/questions/20802669/Windows-Services-and-hooks.html
Thank you all your help.
...Snehanshu
>>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.
https://www.experts-exchange.com/questions/20802669/Windows-Services-and-hooks.html
Thank you all your help.
...Snehanshu
ASKER
>>Thank you all your help.
Thank you all for your help.
:-)
Thank you all for your help.
:-)
ASKER
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