Register for EarthWeb's Million Dollar Sweepstakes!
home account info subscribe login search My ITKnowledge FAQ/help site map contact us


 
Brief Full
 Advanced
      Search
 Search Tips
To access the contents, click the chapter and section titles.

Fast Track Visual C++ 6.0 Programming
(Publisher: John Wiley & Sons, Inc.)
Author(s): Steve Holzner
ISBN: 0471312908
Publication Date: 09/01/98

Bookmark It

Search this book:
 
Previous Table of Contents Next


If the Notepad function is not open, FindWindow() returns NULL, and we should open the Notepad ourselves.

Launching a Windows Program

To start up a program—also called launching a program—we use the Create-Process() function. We need to set up two structures to launch Notepad: a START-UPINFO structure and a PROCESS_INFORMATION structure.

void CJournalView::OnFilePlay()
{
    CWnd* WordPad = FindWindow(NULL, ”Untitled - NotePad”);

    if(WordPad == NULL){

        STARTUPINFO StartupInfo;                                ⇐
        PROCESS_INFORMATION ProcessInfo;                        ⇐
            .
            .
            .

We also need to clear the STARTUPINFO structure. Instead of setting all its members to 0 individually, we use memset() here. Next, we place the size of the STARTUPINFO structure in its own cb member.

void CJournalView::OnFilePlay()
{
    CWnd* WordPad = FindWindow(NULL, ”Untitled - NotePad”);

    if(WordPad == NULL){


Clearing Data Structures

Using memset() is a good way to initialize data structures. You just find the size of the data structure and set that much memory to 0. This saves you the tedious effort of setting each individual variable in a data structure to 0 by name.


        STARTUPINFO StartupInfo;
        PROCESS_INFORMATION ProcessInfo;

        memset(&StartupInfo, 0, sizeof(StartupInfo));               ⇐
        StartupInfo.cb = sizeof(StartupInfo);                       ⇐
             .
             .
             .

Now we’re ready to launch the Notepad program, which we do with CreateProcess(), and to get a pointer to it with FindWindow().

void CJournalView::OnFilePlay()
{
    CWnd* WordPad = FindWindow(NULL, ”Untitled - NotePad”);

    if(WordPad == NULL){

        STARTUPINFO StartupInfo;
        PROCESS_INFORMATION ProcessInfo;

        memset(&StartupInfo, 0, sizeof(StartupInfo));
        StartupInfo.cb = sizeof(StartupInfo);

        CreateProcess(”c:\\windows\\notepad.exe”, NULL,
NULL, NULL, FALSE, 
        0,    NULL, NULL, &StartupInfo, &ProcessInfo);                      ⇐

        WordPad = FindWindow(NULL, ”Untitled - NotePad”);                   ⇐
    }

Now that Notepad is ready, we make sure it has the input focus with the BringWindowToTop() function.

void CJournalView::OnFilePlay()
{
    CWnd* WordPad = FindWindow(NULL, ”Untitled - NotePad”);

    if(WordPad == NULL){
        .
        .
        .
    }

    WordPad->BringWindowToTop();            ⇐
        .
        .
        .
}

We’re ready to play the recorded events into the Notepad program. We keep track of the number of events we’ve played back in a variable named PlayedEvents to make sure we play back all recorded events. We initialize that variable to 0 here:

void CJournalView::OnFilePlay()
{
    CWnd* WordPad = FindWindow(NULL, ”Untitled - NotePad”);

    if(WordPad == NULL){
        .
        .
        .
    }

    WordPad->BringWindowToTop();

    PlayedEvents = 0;                    ⇐
        .
        .
        .

Now we unhook the record hook and place a new playback hook in place instead with SetWindowsHokEx(), connecting that new hook to a new procedure, PlayProcedure().

{
    CWnd* WordPad = FindWindow(NULL, ”Untitled - NotePad”);

    if(WordPad == NULL){
        .
        .
        .


Unhooking Hooks

When you’re done with a hook, unhook it. Leaving hooks in place can degrade performance, and there’s no reason to do so if they serve no purpose.


    }

    WordPad->BringWindowToTop();

    PlayedEvents = 0;
    UnhookWindowsHookEx(Hook);                                       ⇐
    Hook = SetWindowsHookEx(WH_JOURNALPLAYBACK, PlayProcedure,
GetModuleHandle(NULL), 0);                                           ⇐
}

At this point, we’ve installed the record and playback hooks as needed. The next step is to write the two hook procedures, RecordProcedure() and PlayProcedure().

Recording Events

We record keystrokes in the RecordProcedure() function. This function takes three parameters—an event code, wParam, and lParam—and returns a result of type LRESULT. This function is a Windows callback function, which means that Windows calls it after we’ve registered it, so we declare it like this in the view’s header (note that RecordProcedure() is not a member function of the view class):

// JournalView.h : interface of the CJournalView class
//
/////////////////////////////////////////////////////////////////////////////

#if !defined(AFX_JOURNALVIEW_H__6433B67B_A86C_11D1_887F_D42B07C10710__INCLUDED_)
#define AFX_JOURNALVIEW_H__6433B67B_A86C_11D1_887F_D42B07C10710__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
LRESULT CALLBACK RecordProcedure(int nCode, WPARAM wParam, LPARAM lParam);    ⇐
    .
    .
    .

Now add the function itself to the end of the view class’s file, JournalView.cpp.

LRESULT CALLBACK RecordProcedure(int nCode, WPARAM wParam, LPARAM lParam){
    .
    .
    .

Because other hooks may be connected, we first call the CallNextHookEx() procedure, which calls the next hook in the chain.

LRESULT CALLBACK RecordProcedure(int nCode, WPARAM wParam, LPARAM lParam){

    LRESULT Result = CallNextHookEx(Hook, nCode, wParam, lParam);           ⇐
        .
        .
        .

The kind of events we’re looking for have an event code of HC_ACTION. We don’t want to handle other types of events, such as system events, so we make sure nCode is set to HC_ACTION.

LRESULT CALLBACK RecordProcedure(int nCode, WPARAM wParam, LPARAM lParam){

    LRESULT Result = CallNextHookEx(Hook, nCode, wParam, lParam);

    if (nCode != HC_ACTION)                       ⇐
        return(Result);                           ⇐
             .
             .
             .

If nCode is equal to HC_ACTION, lParam is a pointer to an EVENTMSG structure, which looks like this:

        typedef struct tagEVENTMSG {


Why Not WM_CHAR?

You may wonder why we don’t just record WM_CHAR messages and play them back instead of WM_KEYDOWN and WM_KEYUP. The answer is that WM_CHAR is really an artificial message that doesn’t come from Windows itself. It’s a convenience message generated by our Windows program that usually makes key-handling much easier.


            UINT  paramL;
            UINT  paramH;
            DWORD time;
            HWND  hwnd;
        } EVENTMSG;

This is how Windows handles events. For every event, a Windows event structure is created. The first member of this structure, message, holds the type of this message. Because we’re looking for keystroke messages, we record both WM_KEYDOWN and WM_KEYUP messages.


Previous Table of Contents Next


Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.