📄 cb200004mt_f.asp.htm
字号:
<HTML>
<HEAD>
<TITLE>What's Running? </TITLE>
</HEAD>
<BODY>
<TABLE border=0 width="100%" cellpadding=0 cellspacing=0>
<TR valign=top>
<TD width="100%">
<p class=ColumnTitle><font size="2">The API
Calls</font></p>
<p class=ColumnSubtitle>Win32
/ ToolHelp32 / Windows 95 / Windows 98</p>
<p class=BodyText> </p>
<p class=Byline>By Matt
Telles</p>
<p class=BodyText> </p>
<p class=StoryTitle><font size="2">What's
Running?</font> </p>
<p class=StorySubtitle><font size="2">A Task
Manager Clone for Windows 95 and 98</font></p>
<p class=BodyText> <font size="2"> </font> </p>
<p class=BodyText> I've
been working on a project in C++Builder, and I needed the functionality
presented in the Task Manager in Windows NT. (Windows 95 and 98 have similar
lists of tasks available to them, which come up when you press [Ctrl][Alt][Delete].) The specific application would
allow users to create a "Project" of different applications that they want to
launch at the same time. </p>
<p class=BodyText> </p>
<p class=BodyText> I wanted
the ability to show users all of the programs that were running - to capture a
"snapshot" of the current machine state. The idea is to allow people to set up
lists of applications they tend to run together, e.g. I run C++Builder,
Microsoft Word, my screen image capture program, and a few utilities. I'd like
to be able to tell Windows to just start up all of those applications at one
time. Unfortunately, there is no simple way to do this (except at Windows
startup), hence the QuikLaunch application. </p>
<p class=BodyText> </p>
<p class=BodyText> Back to
the problem at hand. In Windows NT, it's relatively easy to get a list of the
currently running applications. However, in Windows 95 and 98, it's a bit more
difficult. You need to use the ToolHelp32 library for help. Before we delve too
deeply into the application code, a little background information about how
Task Manager "knows" what applications are running is in order. </p>
<p class=BodyText> </p>
<p class=BodyText> In
Windows, each application has a "main" window (also known as a top-level
window) and a series of child windows. When I say application, I mean only
those applications that can show themselves on the screen. This is the same
list that Task Manager presents. To find an application, Task Manager first
lists all of the windows on the screen that the system knows about. It then examines
each window to see if it's a top-level window. Once the window qualifies as a
top-level window, the system retrieves the name of the window (the title in the
title bar) and presents it to the user. If this were all I needed, it would be
a simple task. Just loop through all of the windows on the screen, and display
those that are top-level (we will discover exactly how to determine if it's a
top-level window shortly). </p>
<p class=BodyText> </p>
<p class=BodyText> Unfortunately,
I needed a little more than just that. Because I wanted to be able to launch
each one of the applications, I needed to know the actual executable file the
program used to launch the application. This turned out to be a little more
complicated. Let's start with the simplest of problems: listing all of the
top-level windows. </p>
<p class=BodyText> </p>
<p class=Subheads>Working
with the API</p>
<p class=BodyText> C++Builder
VCL components don't provide a way to discover the currently running windows in
the system. In cases like this, the next most natural thing to do is use the
underlying Windows API. Although the VCL provides a nice level of abstraction
and insulation from the ugliness that is the Windows kernel, sometimes you need
to roll up your sleeves and get into it. Fortunately, as Windows API tasks go,
this is a fairly easy one. </p>
<p class=BodyText> </p>
<p class=BodyText> If you
simply want to see the names of all of the windows on the screen, here's a
simple sample program to do just that. Create a new form in C++Builder, and put
a single list box on it. Use the default names for the list box and the form,
i.e. <i>ListBox1</i> and <i
style='mso-bidi-font-style:normal'>Form1</i>, respectively. Then add the code
shown in Figure 1 to the <i>FormCreate</i> event of the form. </p>
<p class=BodyText> </p>
<p class=Code><span class=Code><i><span Class=CodeBlue>// CallBack function for the EnumWindows Windows API
function. </span></i></span></p>
<p class=Code><span class=Code>BOOL CALLBACK
EnumWindowsProc(HWND hwnd, LPARAM lParam) </span></p>
<p class=Code><span class=Code>{</span></p>
<p class=Code><span class=Code> <b> char</b>
szBuffer[ 256 ]; </span></p>
<p class=Code><span class=Code> <b> char</b>
szFileName[ 256 ]; </span></p>
<p class=Code><span class=Code> </span></p>
<p class=Code><span class=Code> <i> <span Class=CodeBlue>// Make sure this is a visible, or iconized, parent window. </span></i></span></p>
<p class=Code><span class=Code> <b> if</b>
((GetWindowLong(hwnd,GWL_HWNDPARENT)==0) && </span></p>
<p class=Code><span class=Code> (IsWindowVisible(hwnd) ||
IsIconic(hwnd))) </span></p>
<p class=Code><span class=Code> { </span></p>
<p class=Code><span class=Code> <i> <span Class=CodeBlue>// Get the window title. </span></i></span></p>
<p class=Code><span class=Code> GetWindowText(hwnd, szBuffer, 256); </span></p>
<p class=Code><span class=Code> <i> <span Class=CodeBlue>// Only add non-blank titles. This will prevent invisible</span></i></span></p>
<p class=Code><span class=Code><i><span Class=CodeBlue> // windows
from showing up. Also eliminates pop-ups. </span></i></span></p>
<p class=Code><span class=Code> <b> if</b>
(strlen(szBuffer)) </span></p>
<p class=Code><span class=Code> { </span></p>
<p class=Code><span class=Code> Form1->ListBox1->Items->Add(szBuffer); </span></p>
<p class=Code><span class=Code> } </span></p>
<p class=Code><span class=Code> } </span></p>
<p class=Code><span class=Code> <i> <span Class=CodeBlue>// You return TRUE from a callback function to continue</span></i></span></p>
<p class=Code><span class=Code><i><span Class=CodeBlue> //
enumerating. </span></i></span></p>
<p class=Code><span class=Code> <b> return</b>
TRUE; </span></p>
<p class=Code><span class=Code>}</span></p>
<p class=Code><span class=Code> </span></p>
<p class=Code><span class=Code><b>void</b> <b
style='mso-bidi-font-weight:normal'>__fastcall</b> TForm1::FormCreate(TObject
*Sender) </span></p>
<p class=Code><span class=Code>{</span></p>
<p class=Code><span class=Code> <i> <span Class=CodeBlue>// Enumerate all windows. </span></i></span></p>
<p class=Code><span class=Code> EnumWindows((WNDENUMPROC)EnumWindowsProc,
0); </span></p>
<p class=Code><span class=Code>}</span></p>
<p class=Captions><b>Figure
1: </b>Building a list
of all top-level windows. </p>
<p class=BodyText> </p>
<p class=BodyText> Now what
is going on here? Well, within our <i>FormCreate</i> method, we're going to
call the <i>EnumWindows</i> API function.
This function will loop through every open window (top-level or not) on the
desktop, and pass the window handle and a user-defined parameter to a callback
function. A callback function is a function in your application to which you
pass a pointer that Windows "calls back" when it wants to let you know about
something. You can think of it as a sort of event handler like the VCL
components use. </p>
<p class=BodyText> </p>
<p class=BodyText> In this
case, the function we're passing to the API function is called <i
style='mso-bidi-font-style:normal'>EnumWindowsProc</i> (that's sort of the
default function name for this particular usage). You'll notice it's defined as
BOOL CALLBACK. Of course, the BOOL part means you must return the Windows
constant True or False. If you return True, the <i>EnumWindows</i> function knows you want to continue processing the
windows. If you return False, the <i>EnumWindows</i>
function knows you're done and stops processing windows. This is handy if you
want to process only a few of the windows. </p>
<p class=BodyText> </p>
<p class=BodyText> Your
callback function will be passed two arguments. The first is the handle of the
window being processed. This is a standard HWND, which is the same as the <i>Handle</i>
property of a VCL window. The second parameter is whatever you passed to the <i
style='mso-bidi-font-style:normal'>EnumWindows</i> API function as the second
parameter. It's called a user-defined parameter. You can store anything here,
as long as it fits into a long value. Any address in Windows will fit here. </p>
<p class=BodyText> </p>
<p class=BodyText> Once we
have the window handle passed to us, we need to see whether this fits the
criteria of a "top-level window." The window doesn't need to have parents (the <i
style='mso-bidi-font-style:normal'>GetWindowLong</i> call), be visible (the <i
style='mso-bidi-font-style:normal'>IsWindowVisible</i> and <i>IsWindowIconic</i> calls), or have a title in its title bar. Once it
passes all of these tests, we'll simply store the name of the window in our
list box. </p>
<p class=BodyText> </p>
<p class=BodyText> Figure 2
shows typical output. As you can see, the list box shows a number of windows,
indicating the programs I happen to have running at that particular instant. </p>
<p class=BodyText> </p>
<p class=Captions><img src="images/cb200004mt_f_image002.jpg"> <br>
<b>Figure 2: </b>A list box shows the currently
running applications. </p>
<p class=BodyText> </p>
<p class=Subheads>Getting the
Executable Name</p>
<p class=BodyText> Now
we're down to the last problem: getting the actual executable file name so that
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -