/*++

Copyright (c) 1994-1998  Microsoft Corporation

Module Name:

    tlist.c

Abstract:

    This module implements a task list application.

Author:

    Wesley Witt (wesw) 20-May-1994
    Mike Sartain (mikesart) 28-Oct-1994  Added detailed task information
    Julian Jiggins (julianj) 19-Mar-1998 Added list processes using specific module feature
    Shaun Cox (shaunco) 9-Jul-1998 Display services running in processes

Environment:

    User Mode

--*/

#include "pch.h"
#pragma hdrstop
#include <dbghelp.h>
#include "psapi.h"


#define BAD_PID     ((DWORD)-1)


DWORD       numTasks;
TASK_LIST   tlist[MAX_TASKS];
BOOL        fShowServices;
BOOL        fShowMtsPackages;

const char *Blanks = "                                                                               ";


VOID Usage(VOID);
VOID PrintThreadInfo(PTASK_LIST pTaskList);
BOOL FMatchTaskName(LPTSTR szPN, LPTSTR szWindowTitle, LPTSTR szProcessName);
VOID GetFirstPidWithName(LPTSTR szTask);

VOID
PrintTask(
    DWORD i
    )
{
    BOOL NameShown = FALSE;
    
    printf( "%4d %-16s", tlist[i].dwProcessId, tlist[i].ProcessName );

    if (fShowServices && tlist[i].ServiceNames[0]) {
        printf( "Svcs:  %s", tlist[i].ServiceNames);
        NameShown = TRUE;
    }

    if (fShowMtsPackages && tlist[i].MtsPackageNames[0]) {
        printf( "%sMts:   %s", NameShown ? "  " : "",
                tlist[i].MtsPackageNames);
        NameShown = TRUE;
    }

    if (!NameShown && tlist[i].hwnd) {
        if (fShowServices || fShowMtsPackages) {
            printf( "Title: %s", tlist[i].WindowTitle );
        }
        else {
            printf( "  %s", tlist[i].WindowTitle );
        }
    }

    printf( "\n" );
}

VOID
PrintTaskTree(
    DWORD level,
    DWORD id
    )
{
    DWORD i;

    DetectOrphans( tlist, numTasks );
    for (i=0; i<numTasks; i++) {
        if (tlist[i].flags) {
            continue;
        }

        // NOTE: The format of the output below should stay fixed forever.  There are tools
        // at MS that depend on it.

        if (level == 0 || tlist[i].dwInheritedFromProcessId == id) {
            printf( "%.*s", level*2, Blanks );
            printf( "%s (%d)", tlist[i].ProcessName, tlist[i].dwProcessId );
            if (tlist[i].hwnd) {
                printf( " %s", tlist[i].WindowTitle );
            }
            printf( "\n" );
            tlist[i].flags = TRUE;
            if (tlist[i].dwProcessId != 0) {
                PrintTaskTree( level+1, tlist[i].dwProcessId );
            }
        }
    }
}

int __cdecl
main(
    int argc,
    char *argv[]
    )

/*++

Routine Description:

    Main entrypoint for the TLIST application.  This app prints
    a task list to stdout.  The task list include the process id,
    task name, ant the window title.

Arguments:

    argc             - argument count
    argv             - array of pointers to arguments

Return Value:

    0                - success

--*/

{
    DWORD           i;
    TASK_LIST_ENUM  te;
    BOOL            fTree;
    BOOL            fFindTasksUsingModule;
    BOOL            fPidOnly = FALSE;
    DWORD           cchPN = 0;
    LPSTR           szPN  = NULL;
    DWORD           dwPID = BAD_PID;
    DWORD                           dwNumServices = 0;
    LPENUM_SERVICE_STATUS_PROCESS   pServiceInfo  = NULL;

    if (argc > 1 && (argv[1][0] == '-' || argv[1][0] == '/') && argv[1][1] == '?') {
        Usage();
    }

    fTree = FALSE;
    fFindTasksUsingModule = FALSE;
    if (argc > 1) {
        if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 't' || argv[1][1] == 'T')) {
            fTree = TRUE;
        } else if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 's' || argv[1][1] == 'S')) {
            fShowServices = TRUE;
        } else if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 'k' || argv[1][1] == 'K')) {
            fShowMtsPackages = TRUE;
        } else if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 'p' || argv[1][1] == 'P') && argc == 3) {
            _strlwr(argv[2]);
            if (!strcmp(argv[2], "system process")) {
                printf("0\n");
                return 0;
            }
            fPidOnly = TRUE;
        } else if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 'p' || argv[1][1] == 'P') && argc == 4) {
            _strlwr(argv[2]);
            if (!strcmp(argv[2], "system")) {
                _strlwr(argv[3]);
                if (!strcmp(argv[3], "process")) {
                    printf("0\n");
                    return 0;
                }
            }
            Usage();
        } else if ((argv[1][0] == '-' || argv[1][0] == '/') && (argv[1][1] == 'm' || argv[1][1] == 'M') && argc == 3) {
            fFindTasksUsingModule = TRUE;
        } else {
            szPN = argv[1];
            if (!(dwPID = atol(szPN)) && szPN[0] != '0' && szPN[1] != 0) {
                dwPID = BAD_PID;
                cchPN = strlen(szPN);
                _strupr(szPN);
            }
        }
    }

    //
    // lets be god
    //
    EnableDebugPriv();

//#if 0 // XXX olegk - enable the block after RI with debugger tree
    //
    // Include 32bit modules in enumeration
    //
    {
        DWORD SymOpt = SymGetOptions();
        SymOpt |= SYMOPT_INCLUDE_32BIT_MODULES;
        SymSetOptions(SYMOPT_INCLUDE_32BIT_MODULES);
    }
//#endif // XXX olegk

    //
    // get the task list for the system
    //
    //
    // Get the process information for all active Win32 services.
    // This allows us to print the service names next to the processes
    // that host them.
    //
    dwNumServices = GetServiceProcessInfo( &pServiceInfo );

    numTasks = GetTaskListEx(
                    tlist,
                    MAX_TASKS,
                    cchPN || (dwPID != BAD_PID),
                    dwNumServices,
                    pServiceInfo);

    free( pServiceInfo );

    if (fShowMtsPackages) {
        AddMtsPackageNames(tlist, numTasks);
    }
            
    //
    // enumerate all windows and try to get the window
    // titles for each task
    //
    te.tlist = tlist;
    te.numtasks = numTasks;
    GetWindowTitles( &te );

    //
    // print the task list
    //
    if (fTree) {
        PrintTaskTree( 0, 0 );
    } else if (fFindTasksUsingModule) {
        PrintTasksUsingModule(argv[2]);
    } else if (fPidOnly) {
        GetFirstPidWithName(argv[2]);
    } else {
        for (i=0; i<numTasks; i++) {
            if ((dwPID == BAD_PID) && (!cchPN)) {
                PrintTask( i );
            }
            else
            if ((dwPID == tlist[i].dwProcessId) ||
                (cchPN && FMatchTaskName(szPN, tlist[i].WindowTitle, tlist[i].ProcessName))) {
                    PrintTask( i );
                    PrintThreadInfo(tlist + i);
            }

            if (tlist[i].pThreadInfo) {
                free(tlist[i].pThreadInfo);
            }
        }
    }

    //
    // end of program
    //
    return 0;
}


VOID
GetFirstPidWithName(
    LPTSTR szTask
    )
/*++
Routine Description:

    Returns the PID of the first task with a Name matching the specified
    Name.  IF no task is found -1 is returned
Arguments:

    szTask    - module name to search for

--*/
{
    DWORD i;
    CHAR szPName[PROCESS_SIZE + 1];
    CHAR szNameWExe[PROCESS_SIZE + 1];

    strcpy(szNameWExe, szTask);
    strcat(szNameWExe, ".exe");

    for (i=0; i<numTasks; i++) {
        strcpy(szPName, tlist[i].ProcessName);
        _strlwr(szPName);

        if ((!strcmp(szPName, szTask))||(!strcmp(szPName, szNameWExe))) {
            if (tlist[i].dwProcessId != 0) {
                printf("%d\n", tlist[i].dwProcessId);
                return;
            }
        }
    }
    printf("-1\n");
}


VOID
Usage(
    VOID
    )

/*++

Routine Description:

    Prints usage text for this tool.

Arguments:

    None.

Return Value:

    None.

--*/

{
    fprintf( stderr,
            "Microsoft (R) Windows NT (TM) Version 5.1 TLIST\n"
            VER_LEGALCOPYRIGHT_STR
            "\n\n"
            "usage: TLIST"
            " <<-m <pattern>> | <-t> | <pid> | <pattern> | <-p <processname>>> | <-k> | <-s>\n"
            "           [options]:\n"
            "           -t\n"
            "              Print Task Tree\n\n"
            "           <pid>\n"
            "              List module information for this task.\n\n"
            "           <pattern>\n"
            "              The pattern can be a complete task\n"
            "              name or a regular expression pattern\n"
            "              to use as a match.  Tlist matches the\n"
            "              supplied pattern against the task names\n"
            "              and the window titles.\n\n"
            "           -k\n"
            "              Show MTS packages active in each process.\n\n"
            "           -m <pattern>\n"
            "              Lists all tasks that have DLL modules loaded\n"
            "              in them that match the given pattern name\n\n"
            "           -s\n"
            "              Show services active in each process.\n\n"
            "           -p <processname>\n"
            "              Returns the PID of the process specified or -1\n"
            "              if the specified process doesn't exist.  If there\n"
            "              are multiple instances of the process running only\n"
            "              the instance with the first PID value is returned.\n\n"
            "\n");
    ExitProcess(0);
}


//
// Routines used to list all processes that have a specific module in use
//

BOOL
FindSpecificModuleCallback(
    LPSTR       Name,
    DWORD_PTR   Base,
    DWORD       Size,
    PVOID       Context
    )

/*++

Routine Description:

    Callback function for module enumeration to find a specific module

Arguments:

    Name        - Module name
    Base        - Base address
    Size        - Size of image
    Context     - User context pointer

Return Value:

    TRUE             - Continue enumeration
    FALSE            - Stop enumeration

--*/

{
    PFIND_MODULE_INFO pFindModuleInfo;

    pFindModuleInfo = (PFIND_MODULE_INFO)Context;

    if (MatchPattern(Name, pFindModuleInfo->szModuleToFind))
    {
        pFindModuleInfo->fFound = TRUE;
        strcpy(pFindModuleInfo->szMatchingModuleName, Name);
        return FALSE; // Found Module so stop enumerating
    }

    return TRUE;
}

BOOL
IsTaskUsingModule(
    PTASK_LIST pTask,
    LPTSTR     szModuleName,
    LPTSTR     szMatchingModuleName
    )

/*++

Routine Description:

    Checks if the given task has the given module loaded

Arguments:

    pTaskList   - task to search for module
    szModule    - module name to search for

Return Value:

    TRUE             - if the module is loaded in the task
    FALSE            - if the module is not loaded in the task

--*/

{
    FIND_MODULE_INFO FindModuleInfo;

    FindModuleInfo.fFound = FALSE;
    FindModuleInfo.szModuleToFind = szModuleName;
    FindModuleInfo.szMatchingModuleName = szMatchingModuleName;

    EnumerateLoadedModules(
        (HANDLE) UlongToPtr(pTask->dwProcessId),
        FindSpecificModuleCallback,
        &FindModuleInfo
        );

    return FindModuleInfo.fFound;
}

void
PrintTasksUsingModule(
    LPTSTR szModuleName
    )

/*++

Routine Description:

    Enumerates through all the tasks in the system looking for those that
    have loaded modules of the given name.

Arguments:

    szModule    - module name to search for

Return Value:

    None

--*/

{
    BOOL fUsed = FALSE;
    DWORD i;
    CHAR szMatchingModuleName[64];

    _strupr(szModuleName); // Needed for wildcarding

    for (i=0; i<numTasks; i++) {
        if (IsTaskUsingModule(tlist + i, szModuleName, szMatchingModuleName)) {
            printf("%s - ", szMatchingModuleName);
            PrintTask( i );
            fUsed = TRUE;
        }
    }
    if (!fUsed) {
        printf( "No tasks found using %s\n", szModuleName );
    }
}


BOOL
GetVersionStuff(
    LPTSTR szFileName,
    VS_FIXEDFILEINFO *pvsRet
    )

/*++

Routine Description:

    Get fixedfileinfo for szFileName.

Arguments:

    szFileName       - name of file
    pvsRet           - fixedfileinfo return struct

Return Value:

    TRUE             - success
    FALSE            - failure

--*/

{
    DWORD               dwHandle;
    DWORD               dwLength;
    BOOL                fRet = FALSE;
    LPVOID              lpvData = NULL;

    if (!(dwLength = GetFileVersionInfoSize(szFileName, &dwHandle))) {
        goto err;
    }

    if (lpvData = malloc(dwLength)) {
        if (GetFileVersionInfo(szFileName, 0, dwLength, lpvData)) {

            UINT                uLen;
            VS_FIXEDFILEINFO    *pvs;
            DWORD               *pdwTranslation;
            DWORD               dwDefLang = 0x409;

            if (!VerQueryValue(lpvData, "\\VarFileInfo\\Translation",
                &pdwTranslation, &uLen)) {
                // if we can't get the langid, default to usa
                pdwTranslation = &dwDefLang;
                uLen = sizeof(DWORD);
            }

            if (VerQueryValue(lpvData, "\\", (LPVOID *)&pvs, &uLen)) {
                *pvsRet = *pvs;
                fRet = TRUE;
            }
        }
    }

err:
    if (lpvData)
        free(lpvData);
    return fRet;
}

BOOL
EnumLoadedModulesCallback(
    LPSTR       Name,
    DWORD_PTR   Base,
    DWORD       Size,
    PVOID       Context
    )

/*++

Routine Description:

    Callback function for module enumeration

Arguments:

    Name        - Module name
    Base        - Base address
    Size        - Size of image
    Context     - User context pointer

Return Value:

    TRUE             - Continue enumeration
    FALSE            - Stop enumeration

--*/

{
    VS_FIXEDFILEINFO    vs;
    CHAR                szBuffer[100];


    szBuffer[0] = 0;
    if (GetVersionStuff( Name, &vs )) {
        wsprintf( szBuffer, "%u.%u.%u.%u %s",
            HIWORD(vs.dwFileVersionMS),
            LOWORD(vs.dwFileVersionMS),
            HIWORD(vs.dwFileVersionLS),
            LOWORD(vs.dwFileVersionLS),
            vs.dwFileFlags & VS_FF_DEBUG ? "dbg" : "shp"
            );
    }
    printf( " %18.18s  0x%08x  %s\n", szBuffer, Base, Name );
    return TRUE;
}

BOOL
PrintModuleList(
    ULONG ProcessId
    )

/*++

Routine Description:

    Prints list of modules in ProcessId

Arguments:

    ProcessID       - process id

Return Value:

    TRUE             - success
    FALSE            - failure

--*/

{
    EnumerateLoadedModules(
        (HANDLE) UlongToPtr(ProcessId),
        EnumLoadedModulesCallback,
        NULL
        );
    return TRUE;
}

DWORD
GetWin32StartAddress(
    HANDLE hThread
    )

/*++

Routine Description:

    Get starting address for thread

Arguments:

    hThread

Return Value:

    Starting Thread address or 0

--*/

{
    NTSTATUS    Status;
    DWORD       ThreadInformation;

    // make sure we have a handle
    if (!hThread)
        return 0;

    // get the threadinfo
    Status = NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress,
        &ThreadInformation, sizeof(ThreadInformation), NULL);
    if (!NT_SUCCESS(Status))
        return 0;

    return ThreadInformation;
}

ULONG
GetLastThreadErr(
    HANDLE hThread
    )

/*++

Routine Description:

    Get Last Error for a Thread

Arguments:

    hThread

Return Value:

    LastError or 0

--*/

{
    TEB                         Teb;
    NTSTATUS                    Status;
    HANDLE                      hProcess;
    ULONG                       LastErrorValue;
    THREAD_BASIC_INFORMATION    ThreadInformation;

    // make sure we have a handle
    if (!hThread)
        return 0;

    // query for basic thread info
    Status = NtQueryInformationThread(hThread, ThreadBasicInformation,
        &ThreadInformation, sizeof(ThreadInformation), NULL);
    if (!NT_SUCCESS(Status))
        return 0;

    // get handle to process
    if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
        (DWORD)(DWORD_PTR)ThreadInformation.ClientId.UniqueProcess))) {
        return 0;
    }

    __try {
        // read the TEB from the process and get the last error value
        if (ReadProcessMemory(hProcess,
            ThreadInformation.TebBaseAddress, &Teb, sizeof(TEB), NULL)) {
            LastErrorValue = Teb.LastErrorValue;
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER) {
    }

    // close the hProcess
    CloseHandle(hProcess);

    return LastErrorValue;
}

BOOL
FPrintPEBInfo(
    HANDLE hProcess
    )

/*++

Routine Description:

    Prints cmdline and cwd of hProcess

Arguments:

    hProcess.

Return Value:

    TRUE             - success
    FALSE            - failure

--*/

{
    PEB                         Peb;
    NTSTATUS                    Status;
    PROCESS_BASIC_INFORMATION   BasicInfo;
    BOOL                        fRet = FALSE;
    WCHAR                       szT[MAX_PATH * 2];
    RTL_USER_PROCESS_PARAMETERS ProcessParameters;

    Status = NtQueryInformationProcess(hProcess, ProcessBasicInformation,
        &BasicInfo, sizeof(BasicInfo), NULL);
    if (!NT_SUCCESS(Status)) {
        SetLastError(RtlNtStatusToDosError(Status));
        return fRet;
    }

    __try {
        // get the PEB
        if (ReadProcessMemory(hProcess, BasicInfo.PebBaseAddress, &Peb,
            sizeof(PEB), NULL)) {
            // get the processparameters
            if (ReadProcessMemory(hProcess, Peb.ProcessParameters,
                &ProcessParameters, sizeof(ProcessParameters), NULL)) {
                // get the CWD
                if (ReadProcessMemory(hProcess,
                    ProcessParameters.CurrentDirectory.DosPath.Buffer, szT,
                    sizeof(szT), NULL)) {
                        wprintf(L"   CWD:     %s\n", szT);
                }

                // get cmdline
                if (ReadProcessMemory(hProcess, ProcessParameters.CommandLine.Buffer,
                    szT, sizeof(szT), NULL)) {
                        wprintf(L"   CmdLine: %s\n", szT);
                }

                fRet = TRUE;
            }
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER) {
    }

    return fRet;
}


// copied from the win32 API code since we need to run on NT 4 and this is a
// new API to NT 5

HANDLE
TlistOpenThread(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    DWORD dwThreadId
    )

/*++

Routine Description:

    A handle to a thread object may be created using OpenThread.

    Opening a thread creates a handle to the specified thread.
    Associated with the thread handle is a set of access rights that
    may be performed using the thread handle.  The caller specifies the
    desired access to the thread using the DesiredAccess parameter.

Arguments:

    mDesiredAccess - Supplies the desired access to the thread object.
        For NT/Win32, this access is checked against any security
        descriptor on the target thread.  The following object type
        specific access flags can be specified in addition to the
        STANDARD_RIGHTS_REQUIRED access flags.

        DesiredAccess Flags:

        THREAD_TERMINATE - This access is required to terminate the
            thread using TerminateThread.

        THREAD_SUSPEND_RESUME - This access is required to suspend and
            resume the thread using SuspendThread and ResumeThread.

        THREAD_GET_CONTEXT - This access is required to use the
            GetThreadContext API on a thread object.

        THREAD_SET_CONTEXT - This access is required to use the
            SetThreadContext API on a thread object.

        THREAD_SET_INFORMATION - This access is required to set certain
            information in the thread object.

        THREAD_SET_THREAD_TOKEN - This access is required to set the
            thread token using SetTokenInformation.

        THREAD_QUERY_INFORMATION - This access is required to read
            certain information from the thread object.

        SYNCHRONIZE - This access is required to wait on a thread object.

        THREAD_ALL_ACCESS - This set of access flags specifies all of the
            possible access flags for a thread object.

    bInheritHandle - Supplies a flag that indicates whether or not the
        returned handle is to be inherited by a new process during
        process creation.  A value of TRUE indicates that the new
        process will inherit the handle.

    dwThreadId - Supplies the thread id of the thread to open.

Return Value:

    NON-NULL - Returns an open handle to the specified thread.  The
        handle may be used by the calling process in any API that
        requires a handle to a thread.  If the open is successful, the
        handle is granted access to the thread object only to the
        extent that it requested access through the DesiredAccess
        parameter.

    NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES Obja;
    HANDLE Handle;
    CLIENT_ID ClientId;

    ClientId.UniqueThread = (HANDLE)LongToHandle(dwThreadId);
    ClientId.UniqueProcess = (HANDLE)NULL;

    InitializeObjectAttributes(
        &Obja,
        NULL,
        (bInheritHandle ? OBJ_INHERIT : 0),
        NULL,
        NULL
        );
    Status = NtOpenThread(
                &Handle,
                (ACCESS_MASK)dwDesiredAccess,
                &Obja,
                &ClientId
                );
    if ( NT_SUCCESS(Status) ) {
        return Handle;
        }
    else {
        return NULL;
        }
}



VOID
PrintThreadInfo(
    PTASK_LIST pTaskList
    )

/*++

Routine Description:

    Prints all kinds of info about a task

Arguments:

    PTASK_LIST of task to print

Return Value:

    None.

--*/

{
    UINT    nThread;
    HANDLE  hProcess;

    // from \\kernel\razzle2\src\ntos\inc\ke.h
    #define MAX_THREADSTATE    (sizeof(szThreadState) / sizeof(TCHAR *))
    static const TCHAR  *szThreadState[] = {
        "Initialized",
        "Ready     ",
        "Running   ",
        "Standby   ",
        "Terminated",
        "Waiting   ",
        "Transition",
        "???       " };

    // get a handle to the process
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pTaskList->dwProcessId);
    if (!hProcess)
        return;

    // print the CWD and CmdLine
    FPrintPEBInfo(hProcess);

    printf( "   VirtualSize:   %6ld KB"
            "   PeakVirtualSize:   %6ld KB\n",
                pTaskList->VirtualSize / 1024,
                pTaskList->PeakVirtualSize / 1024);

    printf( "   WorkingSetSize:%6ld KB"
            "   PeakWorkingSetSize:%6ld KB\n",
            pTaskList->WorkingSetSize / 1024,
            pTaskList->PeakWorkingSetSize / 1024);

    printf( "   NumberOfThreads: %ld\n",
            pTaskList->NumberOfThreads);

    // if we got any threadinfo, spit it out
    if (pTaskList->pThreadInfo) {
        for (nThread = 0; nThread < pTaskList->NumberOfThreads; nThread++) {

            PTHREAD_INFO pThreadInfo = &pTaskList->pThreadInfo[nThread];
            HANDLE hThread = TlistOpenThread(THREAD_QUERY_INFORMATION, FALSE,
                (DWORD)(DWORD_PTR)pThreadInfo->UniqueThread);

            printf("   %4d Win32StartAddr:0x%08x LastErr:0x%08x State:%s\n",
                HandleToUlong(pThreadInfo->UniqueThread),
                GetWin32StartAddress(hThread),
                GetLastThreadErr(hThread),
                szThreadState[min(pThreadInfo->ThreadState, MAX_THREADSTATE - 1)]);

            if (hThread)
                NtClose(hThread);
        }
    }

    // print the modules
    PrintModuleList( pTaskList->dwProcessId );

    // close the hProcess
    CloseHandle(hProcess);
}

BOOL
FMatchTaskName(
    LPTSTR szPN,
    LPTSTR szWindowTitle,
    LPTSTR szProcessName
    )
{
    LPTSTR  szT;
    TCHAR   szTName[PROCESS_SIZE];

    strncpy( szTName, szProcessName, PROCESS_SIZE );
    if (szT = strchr( szTName, '.' ))
        szT[0] = '\0';

    if (MatchPattern( szTName, szPN ) ||
        MatchPattern( szProcessName, szPN ) ||
        MatchPattern( szWindowTitle, szPN )) {
            return TRUE;
    }

    return FALSE;
}