Introduction
In this article we
are going to study how to find the running instances of Internet
Explorer on your machine. This article will also provide an insight on
the windows API and how to call Win32 API functions from C#. You will
also learn how to implement callback functions.
In the code I have
extensively used the Win 32 API functions as shown below:
[DllImport("user32.Dll")]
public static extern int EnumWindows(CallBack x, int y);
[DllImport("User32.Dll")]
public static extern void GetWindowText(int h, StringBuilder s, int nMaxCount);
[DllImport("User32.Dll")]
public static extern void GetClassName(int h, StringBuilder s, int nMaxCount);
[DllImport("User32.Dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, int msg, int wParam, int lParam);
As we go about, I
would explain the need for these different functions. In order to use
the Win32 API
functions like EnumWindows
(..), and the
like we need to import the Win32 DLLs
that implement these
functions.
Win32 API �a
brief explanation
The Windows API has a
number of functions, constants, callback functions, messages and
structures written in C/C++ that you can call and use in your programs.
They perform lower level services on the OS such as memory management,
process switching, disk access etc. They help the application to
interface directly with the processor. Win 32 API
is created for
32-bit processor. It has a platform independent nature. A large part of
the API functions are implemented using some of these libraries like Kernel32.dll
(Operating system kernel functions), User32.dll (user interface
functions) etc.
These DLL (Dynamic Link Library) files are found
wherever the Windows System directory is located. These DLL files
�export� functions thus allowing external programs to use their
functions. You would notice that I have used the concept of handles
in the program. A handle
is an internal structure in Windows that keeps track of the properties
of any object (like windows, buttons, icons, menus, files etc) in the
system. A program is able to manipulate these objects via the handle
to the object. A handle
itself is just a 32-bit integer and its
significance becomes apparent when it is passed as a parameter to an API
function from a program calling those functions.
Implementation
of Win32 API in C#
That
said let us get back to where we had left ourselves. In C#, to access these external
API functions we need the namespace System.Runtime.InteropServices
.
This namespace provides a collection of classes useful for accessing COM
objects, and native APIs. One of the important classes present in it is
the DllImportAttribute
, which is used to define the dll location
that contains the implementation of an extern method like EnumWindows
(..)
of the native system API user32.dll
of Win32 API.
Shown below is the way the code is written in C#:
[DllImport("user32.Dll")]
public static extern int EnumWindows(CallBack x, int y);
extern
keyword indicates that the method is implemented externally. The extern
methods must be declared as static
.
Understanding
Callbacks:
Note the use of a
delegate in the code as shown below:
public delegate bool IECallBack(int hwnd, int lParam);
Let us now study how the callback is
actually implemented. When you click on the button GetIE
, it
calls the event handler GetIE_Click
.
The code is shown below:
private void GetIE_Click(object sender, System.EventArgs e)
{
listBoxHandle = listBox1.Handle;
EnumWindows (new CallBack (IEInstance.EnumWindowCallBack),
(int)listBoxHandle) ;
label1.Text = "Total Instances of Internet Explorer : "+i;
}
private static bool EnumWindowCallBack(int hwnd, int lParam)
{
windowHandle = (IntPtr)hwnd;
listBoxHandle = (IntPtr)lParam;
ListBox lb =(ListBox)ListBox.FromHandle(listBoxHandle);
StringBuilder sb = new StringBuilder(1024);
StringBuilder sbc = new StringBuilder(256);
GetClassName(hwnd,sbc,sbc.Capacity);
GetWindowText((int)windowHandle, sb, sb.Capacity);
String xMsg = sb+" "+sbc+" "+windowHandle;
if( sbc.Length > 0 )
{
if( sbc.ToString().Equals("IEFrame"))
{
myAl.Add(windowHandle);
i++;
lb.Items.Add(xMsg); }
}
return true;
}
The
delegate in our example called IECallBack
takes two arguments: hwnd
(handle to the window) and lParam
(application-defined, any
additional parameter which needs to be passed, in the example we have
passed the handle to the ListBox class). Both the arguments are
integers. We define a function called EnumWindowCallBack (..)
that
matches the signature of the delegate in our program and it is then
called by the API function EnumWindows (..)
to
execute a task. EnumWindows (..)
enumerates through
all existing windows (visible or not) and provides a handle to all of
the currently open top-level windows. Each time a window is located, the
function calls the delegate (named IECallBack
) which in turn
calls the EnumWindowCallBack
(..)
function and passes the window handle to it. This
is because the API function (EnumWindows(..)
can only locate the handle but does not know what to do with it. It
is upon the callback function (EnumWindowCallBack
(..)
) to decide what to do with the handle. It calls API
functions GetWindowText
(..) and GetClassName
(..) to
obtain the title and class name of each window. The EnumWindow
(..) function continues passing the window handles, one at a time, until
all windows have been enumerated, or until the process has been aborted.
If an error occurs, the function returns 0 else a non-zero value.
Enumeration
is just one scenario where callback functions are used. Other common
practice of using them is to create message handler routines for objects
like windows. Hence, an API function will require an associated callback
function whenever it wants the program, calling the API, to do some
necessary tasks. Callback functions return zero to indicate failure and
non-zero values to indicate success. You would notice that we have set
the return value of EnumWindowCallBack
(..)
to True
to continue enumeration.
The function GetWindowText
retrieves the text that appears in the title bar of the regular windows.
It takes three parameters: a
handle
to the window whose title it wants to read, a
String variable that receives
the window�s text, we have taken a StringBuilder
object so that it can have enough room to read variable length strings
and the last parameter is the size in bytes of the string.
The
function GetClassName
retrieves the name of the window class to
which the window belongs. The window class determines a number of
properties common to a group of windows that are inherited from a main
window. Its parameters follow the same explanation as for those of GetWindowText
.
You would see that in the code above we are only concerned with
�IEFrame� type of class names because we are interested in dealing
with Internet Explorer windows only.
You
must have observed the use of the type IntPtr
. It is an integer
whose size varies with the platform (is 32 bit on 32 bit platforms and
64 bit on 64 bit platform). It is used to represent a pointer or a
handle.
Closing an instance of IE
On clicking on the CloseIE
button we are able to close the window containing an instance of
Internet Explorer. Consider the code shown below:
private void RemoveIE_Click(object sender, System.EventArgs e)
{
int index=listBox1.SelectedIndex;
listBox1.Items.RemoveAt(index);
int count =0;
IEnumerator myEnumerator = myAl.GetEnumerator();
while ( myEnumerator.MoveNext() ) {
if ( count == index )
{
listBoxHandle = (IntPtr)myEnumerator.Current;
break;
}
count++;
}
PostMessage(listBoxHandle,0x0010,0,0);
myAl.RemoveAt(count );
label1.Text = "Total Instances of Internet Explorer :" +myAl.Count;
}
Note
that we have used an ArrayList
class (part of the System.Collections
namespace) to hold all the window handles. The moment an item is
selected in the listbox to be removed, we extract the handle of the
window from the ArrayList
and call the PostMessage
function with that window handle and a WM_CLOSE
message. This
closes the open window. We also remove the instance from the ArrayList
and the selected item from the ListBox
.
Conclusion
Hope the above article was useful in explaining the concepts. This
concept has a lot of benefits like changing an instance of IE from your
windows control at run-time.