Tutorial: Capturing and Running IE Instances in a Windows control

Tools Used : VS.NET Final Release

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:

public static extern int EnumWindows(CallBack x, int y);
public static extern void GetWindowText(int h, StringBuilder s, int nMaxCount);
public static extern void GetClassName(int h, StringBuilder s, int nMaxCount);
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#:

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. Have a quick look at the screen-shot before we try to understand how we have done it.


Understanding Callbacks:

Note the use of a delegate in the code as shown below:

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;
//store a listbox handle here.
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;
// getting an instance of listbox from its handle.
ListBox lb =(ListBox)ListBox.FromHandle(listBoxHandle);
StringBuilder sb =
new StringBuilder(1024);
StringBuilder sbc =
new StringBuilder(256);
int)windowHandle, sb, sb.Capacity);
String xMsg = sb+" "+sbc+" "+windowHandle;
if( sbc.Length > 0 )
if( sbc.ToString().Equals("IEFrame"))
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 windows 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;
int count =0;
IEnumerator myEnumerator = myAl.GetEnumerator();
while ( myEnumerator.MoveNext() )
if ( count == index )
listBoxHandle = (IntPtr)myEnumerator.Current;
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.