Link to home
Start Free TrialLog in
Avatar of florisb
florisb

asked on

hook / outlook

Hello there,

Some people (inthe, madshi, ...) already helped me with this problem...

I'm using the code below now in a dll that send a message to my application when Outlook starts. You can see in the code that I also tried to 'catch' the outlook close message, I can't make it work (I want a message if one Outlook window is closed, without sinking the event).

He're is the 'init':
lpHookRec^.TheHookHandle := SetWindowsHookEx(WH_CBT,@KeyBoardProc,hInstance,0);
  end;

Is WH_CBT Okee, in the help I see this is for Computer Based Training.

And here is the code I would like to make working for the close message too:

function KeyBoardProc(code:integer;wparam,lparam:longint):longint;stdcall;
{ Description : The function that actually processes the 'keystrokes' (?) for our hook.
                No error checking because this function is called to often.
  Pre         : HookEventKind has to be set!
  Post        : if Outlook start (not other program): send message to UMS Services.
  Input       : standaard hook variabelen.
  Returns     : longint.
  Creator     : Floris
  Date        : 15 november 1999}
var
res:longint;
cbtcreatewnd:pcbtcreatewnd;
//chtdestroywnd: pcbtcreatewnd; // added
UMSService: HWND;
LPDWORD: longWord; //theadexitcode
begin
res := 0;
if code < 0 then
  result := CallNextHookEx(lpHookRec^.TheHookHandle, code, wparam, lparam)
else if ((code=HCBT_CREATEWND) or (code = HCBT_DESTROYWND) or (code = HSHELL_WINDOWDESTROYED)) then ///is only working for HCBT_CREATEWND...?
  begin
  try
  cbtcreatewnd:=pointer(lparam);
  with cbtcreatewnd^.lpcs^ do
    begin
    res:=0;
    if lowercase(lpszclass)=lowercase('rctrl_renwnd32') then //outlook class heeft een rare naam, ja.
      begin
      res:=1;
      end;
    result:=res;
    end;
  except on exception do
    result:=0;
  end; //try
  end
else
 begin
 result:=0;
 end;

IF RES = 1 THEN
  begin
  //message
  UMSService := FindWindow('TUMS_Service', NIL); //zoek UMS Services
  if UMSService = 0 then
    begin
    ///mogelijkerwijs alsnog proberen te service te herstarten?
    //    MessageBox(0,'UMS Services dll','UMSService not found.',MB_OK);
    //
    // totally disable Outlook?
    //
    end
  else
    begin
    //send message to excutable
    if (code = HCBT_CREATEWND) then
      begin
      SendMessage(UMSService, WM_OUTLOOKSTARTED, 0, 0); ///sending message to own app.
      //kill Outlook proces
      GetExitCodeProcess(lpHookRec^.TheHookHandle,LPDWORD);
      ExitProcess(LPDWORD);
      //mouse bug: reset.
      setCursor(OCR_NORMAL);
      end
    else if ((code = HCBT_DESTROYWND) or (code = HSHELL_WINDOWDESTROYED)) then    //not working yet
      begin
      SendMessage(UMSService, WM_OUTLOOKSTOPPED, 0, 0); ///sending message to own app.
      end;
    end;
  end;
end;

All my available points for it (An I asking too much? I started answering but an now disappointed when +1000 expert points for the show....;-(
Avatar of florisb
florisb

ASKER

an=am
Floris, would you please look here again:

https://www.experts-exchange.com/jsp/qShow.jsp?ta=delphi&qid=10232554 

:-)

About this question: Does it not work at all? Or do you get exceptions? Or what? Please tell us what is working and what is NOT working...

Regards, Madshi.
P.S: If you need more points, you can always buy them...   :-)

https://www.experts-exchange.com/bin/BuyPoints
Avatar of florisb

ASKER

Eeeh, this is the hook question again, not the processes-list...

The code I included is working, but only for the HCBT_CREATEWND, I can't get the DESTROY Working somehow...


Floris
>> Eeeh, this is the hook question again, not the processes-list...

That is right, but you didn't respond on the processes-list-question. Hey, this is experts *EXCHANGE*. That means, if you found a solution for the processes-list-problem, you should share it with us, or at least say us, that you don't want to share it. But not responding is quite unpolite...
Avatar of florisb

ASKER

I didn;t found an answer & added a comment now, I added one before, but that one got lost or something.. ..oops.

No more un-experts?





What do you mean with "No more un-experts?"?

Basically your code looks okay.

      GetExitCodeProcess(lpHookRec^.TheHookHandle,LPDWORD);
      ExitProcess(LPDWORD);
     
This doesn't make sense. You can't ask an exit code of a hook handle. Simply do ExitProcess(0) without calling GetExitCodeProcess before.

Hmm... You're doing 2 things when Outlook is started:
(1) You prevent the creation of this special Outlook window.
(2) You terminate Outlook.

That's okay. But if you do that, you won't get any more messages from Outlook of course, because Outlook is terminated!! What did you expect?
What happens if you remove the ExitProcess call and if you set "result := 0" in the last line of the hook function? Then Outlook should start as normal - do you get the DESTROYWND message then?

And another question: Where does this lpHookRec pointer come from? Is it shared memory or what? A pointer to a memory mapped buffer or what?

Regards, Madshi.
Avatar of florisb

ASKER

>Madshi
Floris

What do you mean with "No more un-experts?"?
>Playing with the unpolite... ...no intension.

GetExitCodeProcess(lpHookRec^.TheHookHandle,LPDWORD);
ExitProcess(LPDWORD);
This doesn't make sense. You can't ask an exit code of a hook handle. Simply do ExitProcess(0) without calling GetExitCodeProcess before.
>I need a handle to the process I want to kill. I really want to kill the whole Outlook process. I seems to work.

Then Outlook should start as normal - do you get the DESTROYWND message then?
>I never get the HCBT_DESTROYWND message

And another question: Where does this lpHookRec pointer come from? Is it shared memory or what? A pointer to a memory mapped buffer or what?
> PHookRec = ^THookRec;
> THookRec = packed record //not really a record now..
>   TheHookHandle : HHOOK;

I do a MapFileMemory in the dllEntryPoint.
Avatar of florisb

ASKER

Thanks so far, wont leave this tread.
>> I need a handle to the process I want to kill. I really want to kill the whole Outlook process. I seems to work.

No no, you DO need a handle if you call TerminateProcess. But you don't need anything for ExitProcess, since ExitProcess simply terminates the CURRENT process. The exitCode parameter, that you give in to ExitProcess has not much meaning. The only meaning of this exit code is, that other processes can ask the value you gave into ExitProcess by calling GetExitCodeProcess. Really, believe me, use ExitProcess(0) and leave the GetExitCodeProcess away... The GetExitCodeProcess call will ALWAYS give you "STILL_RUNNING". That's no process handle...

>> I never get the HCBT_DESTROYWND message

That is really strange... Perhaps I'll find time to test this Sunday afternoon, but not today or tomorrow...   :-(

>> I do a MapFileMemory in the dllEntryPoint.

That is good.
Avatar of florisb

ASKER

You're right about the ExitProcess thing. Somehow I still expected to Exit the whole dll process, but it works perfect for Outlook (with parameter 0).

About killing Outlook, here's an alternative that's very rigorous (?), it works to, but loops all windows

function KillOutlook : boolean; // in dll.
begin
try
///extra beveiliging; kill all Outlook.
EnumWindows(@KillAllOutlookProcesses, 0); //result value not used.
result := true;
except
  result := false;
end; //try
end;

function KillAllOutlookProcesses(Handle : HWND; lparam : LPARAM): boolean; stdcall;
{ Description : Unos tres coolos functie die ge-loop-t wordt vanuit, als de hook geset wordt, worden
                hardhandig alle Outlook processen gekillt.
  Pre         : -
  Post        : Outlook not runnning
  Input       : handle naar zichzelf om EnumWindows te laten loopen, en Lparam dummy.
  Returns     : not used.
  Creator     : Floris
  Date        : 15-11-1999}
var
pName : array [0..MAX_PATH-1] of char;
sName : string;
begin
try
GetWindowText(handle, pname, MAX_PATH);
sName := strpas(pname);
if Pos('OUTLOOK', UpperCase(sname)) > 0 then
  begin
  SendMessage(handle, WM_CLOSE, 0, 0);
  end;
result := true;
except
  result := false;
end; //try
end;



It would be great if you check ths HCBT_DESTROYWND message thing, I could send you some code to be able to test.

About the MapFileMemory; I made another dll with some functions with a TStringList as parameter. I don't do any FileMapping here, but soemthing is going weirdly wrong. Should I use the FileMapping? Now I made the TStringList global in the calling app;
- filling the Tstringlist in the dll works fine. (after call from app with parameter stringlist).
- getting the TSTringList back in the app works fine (without making the parameter var, it does come back).
- Using the TSTringlist again to call another dll functions goes wrong (already on a tstringlist.count, in the dll)...
Do you have short advice?

I will come back here to give you my points, really...:-)

Nice day,
Floris.
>> About killing Outlook, here's an alternative that's very rigorous (?), it works to, but loops all windows

ExitProcess works always, looping through the windows eventually doesn't work or shows popup boxes. So better keep using ExitProcess.

About TStringList and FileMapping:
When hooking something system wide, that means that a copy of your dll is loaded into all processes. So process A has one own copy and process B has one own copy and your process has its own copy of your dll, too. And a pointer that is valid in process A is NOT valid in process B. So your TStringList pointer is valid in your process only (and in the dll copy that is loaded in your process), but not in any other dll copy in any other process.
You MUST use shared memory, e.g. by using memory mapped files.

Regards, Madshi.

P.S: Send me some test code to "madshi@gmx.net".
Avatar of florisb

ASKER

I'm sending you an email.
Floris, this is an extract from the win32 documentation:

HCBT_DESTROYWND
wParam: Specifies the handle to the window about to be destroyed.
lParam: Is undefined and must be set to zero.

So as you can see, the problem with your code is, that you're trying to use lParam as a pointer. That's okay for HCBT_CREATEWND, but not for HCBT_DESTROYWND. In the latter case you can only use wParam (the window handle) to identify the window, that is about to be destroyed.

Regards, Madshi.
Avatar of florisb

ASKER

Sorry Madshi, no answer for me yet...

I change my dll and added a functrion to set the hook kind. I also added some simple filelogging for testing.

I thought I could use the same KeyBoardProc also for the DESTROY... ...but...the function NEVER detects a DESTROY.... ....that's the problem. Once I will detect a destroy, the rest will be Okee.....

Thanks,
Floris.

function KeyBoardProc(code:integer;wparam,lparam:longint):longint;stdcall;
{ Description : The function that actually processes the 'keystrokes' (?) for our hook.
                No error checking because this function is called to often.
  Pre         : HookEventKind has to be set!
  Post        : if Outlook start (not other program): send message to UMS Services.
  Input       : standaard hook variabelen.
  Returns     : longint.
  Creator     : Floris
  Date        : 15 november 1999
  List of updates : 12-12; destroy ingebouwd en re-structured }
var
messageKind:longint;
cbtcreatewnd:pcbtcreatewnd;
//chtdestroywnd: pcbtcreatewnd; // added
UMSService: HWND;
//LPDWORD: longWord; //theadexitcode
begin
s.add('in keyboardproc');
messageKind := 0;
if code < 0 then
  result := CallNextHookEx(lpHookRec^.TheHookHandle, code, wparam, lparam)
else if ((code = HCBT_CREATEWND) and (hookEvent = HCBT_CREATEWND)) then
  begin
  s.add('in create');
  //create, was Outlook?
  try
  cbtcreatewnd:=pointer(lparam);
  with cbtcreatewnd^.lpcs^ do
    begin
    if lowercase(lpszclass)=lowercase('rctrl_renwnd32') then //outlook class heeft een rare naam, ja.
                                                             //see createstruct in Windows SDK; LPCTSTR   lpszClass;
      messageKind:=1;
    result:=messageKind;
    end;
  except on exception do
    result:=0;
  end; //try
  end
else if ((code = HCBT_DESTROYWND) and (hookEvent = HCBT_DESTROYWND)) then
  begin
  //destroy, was Outlook?
  try
  s.add('in destroy');
  //cbtcreatewnd:=pointer(wparam)
  //wparam = longint
  //wparam = handle, is wparam gelijk aan Outlook handle?
  s.add('destroy wparam: '+inttostr(wparam)+ ','+inttostr(longint(lpHookRec^.TheHookHandle)) );
  if (wparam = longint(lpHookRec^.TheHookHandle)) then //handle
    begin
    messageKind := 2;
    end;
  result:=messageKind;
  {HCBT_DESTROYWND
  wParam: Specifies the handle to the window about to be destroyed.
  lParam: Is undefined and must be set to zero.}
  except on exception do
    result:=0;
  end; //try
  end
else
  begin
  //
  result:=0;
  end;
//test UMSService Window / message reveiver
if ((messageKind = 1) or (messageKind = 2)) then
  begin
  //message
  UMSService := FindWindow('TUMS_Service', NIL); //zoek UMS Services
  if UMSService = 0 then
    MessageBox(0,'UMS Services dll','UMSService not found.',MB_OK)
    //mogelijkerwijs alsnog proberen te service te herstarten?
  else
    begin
      ///start or stopped message?
    case messageKind of
      1 : //create
        begin
        PostMessage(UMSService, WM_OUTLOOKSTARTED, 0, 0); ///sending message to own app, post, zodat meteen terug wordt gesprongen (ipv sendMessage)
        //kill Outlook process.
        ExitProcess(0);
        setCursor(OCR_NORMAL); ///nodig voor buggy Outlook.
        end;
      2 : //destroy
        begin
        MessageBox(0,'UMS Services dll','Stop send.',MB_OK);
        PostMessage(UMSService, WM_OUTLOOKSTOPPED, 0, 0); ///sending message to own app.
        end;
      end; //case
    end;
  end;
end;


procedure StopKeyBoardHook; stdcall;
{ Description : Stop hooking.
  Pre         : If we have a process wide memory variable and the hook has already been set...
  Post        : -
  Input       : -
  Returns     : -
  Creator     : Floris
  Date        : 15 november 1999}
begin
s.savetofile('c:\ums3.text');
if ((lpHookRec <> NIL) AND (lpHookRec^.TheHookHandle <> 0)) then
  begin
  {Remove our hook and clear our hook handle}
  if (UnHookWindowsHookEx(lpHookRec^.TheHookHandle) <> FALSE) then
    begin
    lpHookRec^.TheHookHandle := 0;
    end;
end;
end;


procedure StartKeyBoardHook; stdcall;
{ Description : Start hooking.
  Pre         : SetHookEventKind.
  Post        : -
  Input       :
  Returns     :
  Creator     : Floris
  Date        : 15 november 1999}
begin
s := tstringlist.create;
s.add('new run');
MessageBox(0,'UMS Services dll','new stringlist',MB_OK);

if ((lpHookRec <> NIL) AND (lpHookRec^.TheHookHandle = 0)) then
  begin
  //Set the hook and remember our hook handle
  lpHookRec^.TheHookHandle := SetWindowsHookEx(WH_CBT,@KeyBoardProc,hInstance,0);
  end;
end;



procedure SetHookEventKind(eventInt : integer); stdcall;
{ Description : Make hook switch between HCBT_CREATEWND and HCBT_DESTROYWND.
                Windows unit:
                  EXTERNALSYM HCBT_CREATEWND
                  HCBT_CREATEWND = 3;
                  EXTERNALSYM HCBT_DESTROYWND
                  HCBT_DESTROYWND = 4;
  Pre         : Hook installed and running.
  Post        : -
  Input       :
  Returns     :
  Creator     : Floris
  Date        : 17 november 1999}
begin
//stop hook
//StopKeyBoardHook;
//set hook kind
if eventint = HCBT_CREATEWND then
 begin
 hookEvent := HCBT_CREATEWND;
 end
else if eventint = HCBT_DESTROYWND then
 begin
 hookEvent := HCBT_DESTROYWND;
 end;
//start hooking again
//StartKeyBoardHook;
end;
Floris, please reread this paragraph from my last comment (that one before my answer) again:

"About TStringList and FileMapping: "

You MUST NOT use the string list in the dll!!! You MUST NOT use "s.add('something')" in the hook function, because the hook function is called in the context of Outlook, and Outlook has no access to your string list!!!
Same with the "hookEvent" variable. The dll instance that is loaded in Outlook has no access to this variable. You must include this hookEvent variable to the record you share by using file mapping.

Regards, Madshi.

P.S: Please remove the "try..except" in the "destroy part" of your hook function. If you get an exception then, you made something wrong...
Avatar of florisb

ASKER

Hmmm, I added the TStringList only for debugging, I'll skip it, but I did get a textfile on my disk... ...weird?

The TStringList mapping question was for the other dll. I call this dll with a tstringlist used to build a new .pst. Everything works fine, until I free the dll or leave the function that's calling this dll (-< Access Violations).

I'll try a try..except, but It seems that HCBT_DESTROYWND is never recognized... Is is not a mistake to use the same keyboardproc for both a create and a destroy?

I was also surprised that the Dll_Process_Attach- case, in the DllEntryPoint is called more than once.

I don't understand a copy of my dll is loaded in all processes...

My problem is that I miss some dll / hook theory. I did some reading, and will continue now with some dll reading...

Some more points.

Greetings,
Floris.
Okay, a short explanation about the win32 system:
Each process in win32 has its own memory/address context from $00000000 - $FFFFFFFF. Windows maps the memory to the real RAM or to the swap file on the hard disc.
That means that process A can have a memory block allocated at address $12345678, and process B can have a DIFFERENT memory block at the same address!!
Do you understand that? A pointer is only valid in it's own process. A pointer that is valid in process A is NOT valid in process B. Process A can't even access the memory of process B. That's true system security! This way process A can't do anything to crash process B.
Okay, now dlls:
What is a dll? A dll is simply a "code extension" to the executable. Now when your application runs, it loads your hook dll. That means, your executable file is found in your process' address/memory context at position X (most of the time $40000), and the dll is found in your address/memory context at position Y.
Now Outlook starts and tries to open a window. As a result Windows tries to call your dll in the memory context of Outlook. But since Outlook can't reach the dll code that is loaded in YOUR process, Outlook must load it's own "copy" of your dll into its memory. That means, that your dll is loaded into several processes, thus you get multiple ATTACH_PROCESS messages.
Now the dll in Outlook doesn't have a valid string list pointer, because the string list pointer is only valid for your process!!
And all variables that all processes must know, must be in a memory area, that is shared between all processes. Thus you need something like memory mapped files.

Wow, really complicated!!

>> Hmmm, I added the TStringList only for debugging, I'll skip it, but I did get a textfile on my disk... ...weird?

Well, the dll copy that is loaded by your process, works alright. But all the other dll copies loaded in the other processes don't work. They'll throw out an exception, that then is caught by the "try..except" in your hook-functions.

>> I'll try a try..except, but It seems that HCBT_DESTROYWND is never recognized...

*NO*, I meant, you should REMOVE the try..except. You already have a try..except in your hook function. Please remove it!!
Because this try..except only hides your programming faults!

>> Is is not a mistake to use the same keyboardproc for both a create and a destroy?

That is no problem...

Regards, Madshi.
Avatar of florisb

ASKER

Thanks so far.

Yeah yeah, everything you understand isn't complicated anymore. But: don;t forget the 'mental model', people create this in their head and then they THINK they understand it all... ...well, they do as long as the mental model is correct for the given situation....:-)....

I'll continue tomorrow and give you the points as soon as it works (or should I already do it for all the explanations / time?....)

Stuck on an other problem today; the other dll. In that one I use a list of records... ...crashing all the time after running all functions perfect. It works without error when I removed the loop that free's all records and the list... ...hmmmm, well, tomorrow.

Floris.
Give me the points, when it works. The explanation/time is a present this time.

BTW, you shouldn't have accepted the answer in the other question (that one about hiding the process under NT). Because accepting a wrong answer is a waste of points for you...   :-)

Regards, Madshi.
Avatar of florisb

ASKER

Yep... ....I will.

Yep...


Here another comment; just something I find weird, read it if you like.

In ANOTHER dll, I have a global var:
  EntryLookUp : TList;
And a type:
type
  FolderRecord = ^ARecord;
  ARecord = record
    Path: String;
    EntryID: wideString;
    mapi_folder : MAPIFolder;
  end;

I thought that when I detach from the dll, I should clean the memory with something like this:
for B := 0 to (EntryLookup.Count - 1) do
  begin
  Folderrec := EntryLookup.Items[B];
  Dispose(Folderrec);
  end;
EntryLookup.Free;

This result in 'too many consecutive errors'.

When I don't clean the memory, everything works perfect. I don't get it and am afraid for memoryleaks (my VC++ college is always talking about this). Does Delphi do the cleaning automatically when I free the library? I didn;t think so, with lists of pointers.....?

Good day,
Floris.
No, Delphi does not automatically free classes and pointers, but neither does C++!!!

Your code looks okay to me. How do you fill the EntryLookup list? Which type does the variable Folderrec have?

Regards, Madshi.
Avatar of florisb

ASKER

VC++ has a lott more problems with this. After finishing for example a VC++ COM object, you (well, some developer's...) need a lott of fine-tuning to minimize (!) memory leaks. Delphi seems to do a lott of things automatically.


FolderRec : FolderRecord; //record voor entryLookup

procedure entryLookUpAdd(folder:string;FolderTemp: MAPIFolder);
{ Description : used by BuildUMSFolders, zie aldaar.
  Creator     : Floris
  Date        : 8-12-1999 }
begin
New(Folderrec);
Folderrec^.path := Folder;
Folderrec^.entryID := folderTemp.Get_EntryID;
Folderrec^.mapi_Folder := folderTemp;
entryLookup.Add(Folderrec);
//Dispose(Folderrec);
end;

I also get problems when I dispose the folderrec. Not with this procedure in a normal project, but only when in a dll.

I call this procedure from a loop where I fill a new pst with a folderstructure.

Floris.
The code looks alright to me. Perhaps you're giving in a string into this entryList that was allocated in the application, rather then the dll?
How does the definition of the exported functions look like?

Yes, Delphi deallocated interfaces/COM objects/strings/dynamic arrays automatically - I *love* this feature!!   :-))

Regards, Madshi.
Avatar of florisb

ASKER

Hi Madshi,

I first continue with the Destroy hook, somehow this continues to be a problem for me...

okee:
definition of exported functions... ..hmmm, you mean:
exports
//te exporteren functies.
KillOutlook name 'KillOutlook',
CheckPst name 'CheckPst',
StartInvisible name 'StartInvisible',
createNewPstFile name 'createNewPstFile',
BuildUMSFolders name 'BuildUMSFolders',
BuildUMSItems name 'BuildUMSItems',
DisplayInbox name 'DisplayInbox';
(all exports are without parameters now....:-)

Or stdcall? I deleted this statement too...

I send you this dll last Friday (with errors!).

F.
Avatar of florisb

ASKER

By the by... ...in the hook dll I should compare the wparam with a handle to Outlook... ...I remeber. The wparam is a longint... ...I can't really figure out how to get the if... ..statement right.

This is nonsense:

if (wparam = longint(lpHookRec^.TheHookHandle)) then
    begin
    messageKind := 2;
    end;

But, aaaah!

He, I got 5 more points again!

Thanx,
Floris.
Avatar of florisb

ASKER

he, I deleted an other question... ....I'm getting a bit frustrated... ...so, more points!

Madshi? Do you have some more about the Destroy hook.....:-)

Sincerely,
Floris.
The hook handle is the result from SetWindowsHookEx, right? Then you can't compare it with wParam, since wParam is the WINDOW handle. That are two different things.

I wanted to see the definition WITH the parameters... The parameters are the important part...

Regards, Madshi.
Avatar of florisb

ASKER

Hook dll:
{HCBT_DESTROYWND
wParam: Specifies the handle to the window about to be destroyed.
  lParam: Is undefined and must be set to zero.}

So I try things like:
  cbtcreatewnd:=pointer(wparam);
  with cbtcreatewnd^.lpcs^ do
    begin
    if lowercase(lpszclass)=lowercase('rctrl_renwnd32') then
                                              messageKind:=2;
    result:=messageKind;
    end;

I'm not really into handle and pointer as you see.....;-(



Other dll:
Like;
function CheckPst: boolean;
begin
try
result := true;
if (FileExists(path) = true) then
   deletefile(path);
except
  result := false;
end; //try
end;

But this dll is working good enough for the demo version. I'm concentrating now on the hook...

F.

ASKER CERTIFIED SOLUTION
Avatar of Madshi
Madshi

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 florisb

ASKER

Thanks!

free afternoon... ...leaving now... ...checking tomorrow.

:-)

Floris.
Avatar of florisb

ASKER

YES!!!!!!!!!!!!!!!!!!!!!

It's working Madshi! (I start to find myself stupid now, but I try to block that thought).

I give you an Excellent! grade, because
- it's working.
- you gave me a lott of extra information, that was quite usefull.

I wish you good luck and you'll probably find me again asking questions here...:-) One day I might answer one of your questions....errrrr....?-)

No I'll continue with refining the hooks; things like: what if a user opens 3 Outlook windows? And what with Outlook continuing being in the processes list after closing? And what if a user uses send to | mail recepient?
Will be fun... ...

Greetings,
Floris.





:-)
Avatar of florisb

ASKER

Hi Madshi,

I have one other question for you, I could start another thread for it, but it's a short one... ...I hope you don't mind.

In the hooking dll, I want to use an integer te set the hookkind (create / destroy) that has to be detected.

I suppose I can't just use a global integer, I should use an integer pointer and FileMap it too.

Could I define the integer with:
hookEvent : ^integer;
map it with
hookEvent := MapViewOfFile(hObjHandle,FILE_MAP_WRITE,0,0,dwAllocSize);
set it with:
hookevent := pointer(HCBT_CREATEWND);
And check for it in an if.. ..statement with:
(hookEvent = pointer(HCBT_CREATEWND))

I'm bad at pointers... ...I'll just try...

Greetings,
Floris.
Well, you already have a record that is shared with file mapping, haven't you? Why not simply adding this integer value to that record?

If you don't want that, please use this instead of your code:

hookEvent^ := HCBT_CREATEWND;

if hookEvent^ = HCBT_CREATEWND then ...

Regards, Madshi.
Avatar of florisb

ASKER

Yes, offcourse. I can add it to the record...:-)...didn't think of it, but it's the logical solution.

I just made a small fileMapping project and dll for the integer, seems that I learned some things from this communication, it worked!

Thanks again!

Sincerely,
Floris.
Avatar of florisb

ASKER

I'm so bold to ask one more question... ...please tell me when to start a new thread....:-)

Took me a while, but it's now working perfect, as long as you only open one Outlook window. I tried to add an Outlook-window-counter at the place where I send a message to my app that Outlook has been closed. I do use FileMapping for it, and I set the integer to 0 every time, but the integer is one more every time I open en close Outlook again.

An alternative would be to always detect Outlook windows creation (Now I switch between create and destroy detection) and inc the counter in the record. And then to check when a destroy is detected whether this was the last window.

Maybe you could look at my code, I tried it in a seperated test project first; there it worked:

//some code here I copied earlier from Experts-exchange... ...it might be yours...

function CheckForOutlookProcesses(Handle : HWND; lparam : LPARAM): boolean; stdcall;
var
pName : array [0..MAX_PATH-1] of char;
sName : string;
begin
try
GetWindowText(handle, pname, MAX_PATH);
sName := strpas(pname);
//Als window van Outlook is dan teller ophogen, 1 Outlook instantie resulteert in 2 (baseclass + explorer ofzo).
if Pos('OUTLOOK', UpperCase(sname)) > 0 then
  begin
  inc(lpHookRec^.Outlooks);
  end;
result := true;
except
  result := false;
end; //try
end;


function lastOutlookWindow : boolean;
{ Description : Is the closed Outlook the last Windows?
  Pre         : -
  Post        : -
  Input       : -
  Returns     : -
  Creator     : Floris
  Date        : 15 decamber 1999}
begin
lpHookRec^.Outlooks := 0;
EnumWindows(@CheckForOutlookProcesses, 0);
if lpHookRec^.Outlooks = 3 then //in my test project with one Outlook window this counter is 2, in the dll this counter starts at 4, then it's 5, 6, etc.
  result := true
else
  result := false;
end;

In the keyboardproc when the destroy is detected, this code:
        if lastOutlookWindow then
          begin
          //lpHookRec^.hookEvent := 0;
          PostMessage(UMSService, WM_OUTLOOKSTOPPED, 0, 0); ///sending message to own app.
          ExitProcess(0);  //really kill it... ...
          setCursor(OCR_NORMAL); ///nodig voor buggy Outlook.
          end

I don;t understand why the Outlooks is 3,4,5,6,etc. I'm definitely doing something very wrong.

Thanks a lot if you could give me one last hint....

Greetings,
Floris.
Avatar of florisb

ASKER

errrr. And when code < 0 I call CallNextHookEx, but when the code is CREATE and not from Outlook I don't call it... ...I don't understand why... ...and I did read the help.

For completeness I copy the complete keyboardproc here:

function KeyBoardProc(code:integer;wparam,lparam:longint):longint;stdcall;
{ Description : The function that actually processes the 'keystrokes' (?) for our hook.
                No error checking because this function is called to often.
  Pre         : HookEventKind has to be set!
  Post        : if Outlook start (not other program): send message to UMS Services.
  Input       : standaard hook variabelen.
  Returns     : longint.
  Creator     : Floris
  Date        : 15 november 1999
  List of updates : 12-12; destroy ingebouwd en re-structured
                    15-12; destroy hook eindelijk werkend gekregen. }
var
messageKind:longint;
cbtcreatewnd:pcbtcreatewnd;
//chtdestroywnd: pcbtcreatewnd; // added
UMSService: HWND;
s1 : string;
//LPDWORD: longWord; //theadexitcode
begin
//s.add('in keyboardproc');
result := 0;
messageKind := 0;
if code < 0 then
  begin
  result := CallNextHookEx(lpHookRec^.TheHookHandle, code, wparam, lparam);
  end
else if ((code = HCBT_CREATEWND) and (lpHookRec^.hookEvent = HCBT_CREATEWND)) then
  begin
//  s.add('in create');
  //create, was Outlook?
  try
  cbtcreatewnd:=pointer(lparam);
  with cbtcreatewnd^.lpcs^ do
    begin
    if lowercase(lpszclass)=lowercase('rctrl_renwnd32') then //outlook class heeft een rare naam, ja.
      begin                                                      //see createstruct in Windows SDK; LPCTSTR   lpszClass;
      messageKind:=1;
      result:=messageKind;
      end;
    end;
  except on exception do
    result:=0;
  end; //try
  end
else if ((code = HCBT_DESTROYWND) and (lpHookRec^.hookEvent = HCBT_DESTROYWND)) then
  begin
  //destroy, was Outlook?
  try
  SetLength(s1, MAX_PATH + 1);
  SetLength(s1, GetClassName(wParam, pchar(s1), MAX_PATH)); //HOERA! Het Werkt! Anders dan de create dus... ...wat een ramp was dit zeg.
  if lowercase(s1) = 'rctrl_renwnd32' then
    begin
    messageKind:=2;
    result:=messageKind;
    end;
  except on exception do
    result:=0;
  end; //try
  end;

///if lpHookRec^.hookEvent^ = 0 then no message will be sent to UMS Service

//what kind of message is send?
if ((messageKind = 1) or (messageKind = 2)) then
  begin
  //message
  UMSService := FindWindow('TUMS_Service', NIL); //zoek UMS Services
  if UMSService = 0 then
    MessageBox(0,'UMS Services dll','UMSService not found.',MB_OK)
    //mogelijkerwijs alsnog proberen te service te herstarten?
  else
    begin
      ///start or stopped message?
    case messageKind of
      1 : //create
        begin
        PostMessage(UMSService, WM_OUTLOOKSTARTED, 0, 0); ///sending message to own app, post, zodat meteen terug wordt gesprongen (ipv sendMessage)
        //kill Outlook process.
        ExitProcess(0);
        setCursor(OCR_NORMAL); ///nodig voor buggy Outlook.
        end;
      2 : //destroy
        begin
        //hier zou er ook voor gekozen kunnen worden Outlook niet te killen / free hook ofzo,
        //UMS kan dan allerlei acties uitvoeren en zelf Outlook afsluiten.
        if lastOutlookWindow then
          begin
          //lpHookRec^.hookEvent := 0;
          PostMessage(UMSService, WM_OUTLOOKSTOPPED, 0, 0); ///sending message to own app and wait till returned.
          ExitProcess(0);  //really kill it... ...
          setCursor(OCR_NORMAL); ///nodig voor buggy Outlook.
          end
        else
//          CallNextHookEx(lpHookRec^.TheHookHandle, code, wparam, lparam);
          //lpHookRec^.hookEvent := HCBT_CREATEWND;
          //StartKeyBoardHook; ///gebeurt in exe.
//          end;
        end;
      end; //case
    end;
  end;
end;

I do hope not to ask too much.... ....

Sincerely,
Floris.

You're asking all top-level windows. Outlook surely has more than one top-level window. I don't know in how many of the windows the name "outlook" appears. That's coincidence, I guess. So with one running Outlook instance this count could be 1..x, depending on how many windows/dialog boxes/mail edit windows you've opened.
Why is the count in your dll wrong? Probably, because you miss some events because of a wrong "hookEvent" variable. Let's say you set the hookEvent variable to HCBT_CREATEWND. Now Outlook is started and creates 4 windows. Then it destroyes 2 windows again. You don't handle the destroy events, because your hookEvent variable is still set to HCBT_CREATEWND.
So my suggestion/question: Why having such a variable??? IMO you should handle ALL events, I think such a "mode" variable makes only problems...

Regards, Madshi.
Avatar of florisb

ASKER

You write about handling the destroy events. Here I miss some theory I suppose...

If Code < 0 then CallNextHookEx.

If HCBT_DESTROYWND and 'rctrl_renwnd32' then MessageOutlookStarted.

But if HCBT_DESTROYWND and not 'rctrl_renwnd32' then Nothing. Should something be handled here? I see how my code works, but miss the background info for this. If something should be handled here my code is buggy...

I use a mode because I try do perform specific series of actions when Outlook is Openened / Closed (I do know about the sinking alternative). I don't want the Open message to be send when the 'destroy-mode' is active. I always try to avoid modes, but in this case it seemed a solution...
And somehow I have to detect when the last Outlook window is being closed... ....it seemed that counting windows is an option here...

I'll now make another version of my project doing an inc on the counter in the CREATE en an dec on the destroy. Let's see how this works, with this I could stop using modes. I hope the values of my counter (it will be fileMapped), won't surprise me like that Outlook counter...

Now, when the OutlookStopped message is sended to my app. I do this:
StopKeyBoardHook; //UnHookWindowsHookEx
OutlookExitProcedure; //own procedure.
lpHookRec^.hookEvent := HCBT_CREATEWND; //switch mode
StartKeyBoardHook; //SetWindowsHookEx

So there the time between the Stop en StartHook No hook is detected... ...I hope I'm not making a mistake to stop and restart te hook... ...works Okee now...?

Thanks,
Floris.

Avatar of florisb

ASKER

Hi Madshi,

I really appreciate your comments, but I also start another thread looking for some-one who hooked Outlook before.

Good day,
Floris.