IE add-in development in VB.NET, C#:
tabbing, windowing and threading, instancing

Add-in Express™
for Internet Explorer® and Microsoft® .net

Add-in Express Home > Add-in Express for Internet Explorer > Online Guide > How an IE add-on works: tabs, windows, threads, instancing

How your add-on loads into IE

Shims

Internet Explorer is unmanaged while an Add-in Express based add-on is a managed class library. Therefore, there must be some software located between IE and your add-on; the software, or the shim, follows the rules for IE add-ons and, at appropriate moment, loads the .NET Framework and starts the add-on.

Add-in Express Loader

An Add-in Express based IE add-on is supplied with a special precompiled shim - Add-in Express Loader. The loader supplied as 32-bit and 64-bit DLLs (adxloader.dll, adxloader64.dll) is designed to fulfill the following basic tasks: it loads the .NET Framework, creates a unique AppDomain for the add-on, and loads the add-on into the AppDomain. Advanced options are provided to the developer by the manifest file (adxloader.dll.manifest) that may have the following look:


			  <?xml version="1.0" encoding="utf-8"?>
				<?xml version="1.0" encoding="utf-8"?>
				<configuration>
				  <assemblyIdentity name="MyIEAddon14, PublicKeyToken=f9f39773da5c568a" />
				  <loaderSettings generateLogFile="true" shadowCopyEnabled="true"
								  privileges="administrator" configFileName="app.config"
								  clrVersion="{major[[.minor].build]}">
					<logFileLocation>C:\MyLog.txt</logFileLocation>
				  </loaderSettings>
				</configuration>

				

The loader allows generating a log file containing useful information about errors on the add-in loading stage. The file is located here: My Documents\Add-in Express\adxloader.log. Also, you can disable the Shadow Copy feature of the Add-in Express Loader, which is enabled by default (see Deploying - shadow copy?). The privileges attribute accepts the "user" string indicating that the installer generated by an Add-in Express based setup project can be run with non-administrator privileges. The only user-installable IE extension type is IE Bar. All other project types require using the "administrator" string for this attribute.

When an Add-in Express based project is being built, the loader files are copied from the Loader folder of your project to the output directory of the setup project(s).

How your IE add-on works

On using Add-in Express for IE modules

The version of IE is available in theModule.IEVersion. The IE application object (it's a COM object) is available in theModule.IEObj. For the document loaded in the current IE tab, see theModule.HTMLDocument; it returns an object of the mshtml.HTMLDocument type, it's a COM object, too.

Note that COM objects (and all classes defined in mshtml are COM classes) cannot cross the process boundaries. That is, when implementing any sort of communication between instances of your module, you may need to create a method returning information provided by a COM object rather than using the COM object directly in your code. For example, consider an add-on that deals with documents opened in different IE tabs. If such an add-on acquires HTMLDocument.documentElement from another module instance and uses it directly, this can result in an exception because HTMLDocument.documentElement returns a COM object and no COM object can cross the boundaries of the process in which it was created. The approach allowing you to avoid such an exception can be found in the code of the Advanced Search sample add-on (download it here ): check how the GetHTMLText property is defined and used.

IEModule plus IE bars and toolbars: there and back again

To access the IE bar (IE toolbar) from your IE module, you use theModule.Bars (theModule.Toolbars) to get a collection of ADXIEBarItem (ADXIEToolBarItem) objects; every object provides access to the BarObj (ToolBarObj) property, which returns the corresponding ADXIEBar (ADXIEToolbar). For instance, the code below demonstrates calling a public method defined in an ADXIEBar from the IE module:

VB.NET

				Dim barItem As AddinExpress.IE.ADXIEBarItem = Me.Bars(0)
				Dim barObj As AddinExpress.IE.ADXIEBar = barItem.BarObj
				Dim myBar As MyIEAddon1.MyIEBar1 = CType(aBar, MyIEAddon1.MyIEBar1)
				myBar.MyMethod()
			 
C#
				
				AddinExpress.IE.ADXIEBarItem barItem = this.Bars[0];
				AddinExpress.IE.ADXIEBar barObj = barItem.BarObj;
				MyIEAddon1.MyIEBar1 myBar = barObj as MyIEAddon1.MyIEBar1;
				myBar.MyMethod();				
			

To access the IE module form the IE bar (IE toolbar), use ADXIEBar.Module (ADXIEToolbar.Module).

Tabbing

The ADXIEModule.IsTabbedBrowsingEnabled property informs you whether tabbing is enabled or not. When the user opens a tab, the OnTabCreated and then OnTabActivated events are raised. At appropriate moments OnTabMoved and OnTabClosed occur. You can activate any given tab programmatically only if ADXIEModule.IsQuickTabsEnabled is true; this means that the Quick Tabs feature of IE7, IE8 and IE9 is enabled. To activate a tab, you call ADXIEModule.TabActivate; this causes the OnTabChanging and OnTabChanged events to fire. In IE6, you use the OnConnect event of the module to get to know when a window is opened and use the HTML Events component to handle the OnActivate event of the Window HTML element.

Note that all tab-related methods work asynchronously. The tab-related methods are TabActivate, TabFirst, TabLast, TabNext and TabPrevious methods that activate the current, first, last, next and previous tabs respectively. Another tab-related method is TabClose. It closes the current tab (also asynchronously). All the methods above are available in ADXIEModule only.

Windowing and threading

IE opens its windows in separate threads. Once an IE window is opened, IE creates an instance of your IE module, toolbar (IE toolbar module), or IE bar (IE bar module); the instance fires the OnConnect event; the instance fires the OnDisconnect event when the current tab (window in IE6) is closing. You can identify an instance of the IE module by the ID of the thread that opens that tab (window). Below you will find how different IE versions open their windows.

IE6 opens IE windows in several threads running within several processes.

IE7 may open several main windows in the same or in different processes if tabbing is enabled and depending on the way you open IE windows; each main window contains tab windows running in threads of their own.

IE8 and IE9 may open several main windows. Each of them runs in several threads of the same process and contains tab windows that run in threads of their own if tabbing is enabled and depending on the way you open IE windows as well on the operating system that you use.

And here are some useful properties:

  • Process ID - theModule.ProcessID
  • Thread ID - theModule.ThreadID
  • IE main window - theModule.MainWindowHandle
  • IE tab window - theModule.ParentHandle; in IE6, this property returns the same value as theModule.MainWindowHandle
  • Tabbing is enabled - ADXIEModule.IsTabbedBrowsingEnabled
  • IE Module - ADXIEBar.Module, ADXIEToolbar.Module

Instancing

In whatever way a tab (or window in IE6) is opened, this creates a new instance of the add-on (ADXIEModule), toolbar (ADXIEToolbar, ADXIEToolbarModule) or IE bar (ADXIEBar, ADXIEBarModule). This also creates new instances of main menu items and context menu items; all of them work in the same thread identifiable via theModule.ThreadID. The only thing that works in another thread or even process (IE8, IE9) is the commands that you add onto the Command Bar toolbar (IE7, IE8 and IE9); an instance of a command is created for every main IE window identifiable via theModule.MainWindowHandle.

So, the module of your IE extension is created for every IE tab. The total number of module instances is available in the GetModuleCount property (GetBarCount,GetToolbarCount). You can access any instance using either GetModuleByIndex or GetModuleByThread (GetBarByIndex, GetBarByThread, GetToolbarByIndex, GetToolbarByThread); make sure that you supply these methods with proper values provided by GetModuleIndex (GetBarIndex, GetToolbarIndex) and ThreadID.

You can call any public property or method of any module instance even if it runs in another process. This is implemented via Remoting. For that reason, such calls are synchronous.

Note.Mshtml-related classes cannot cross process boundaries; instead of returning such an object in a property/method, you need to return the actual information needed such as the URL string or HTML code.

The sample code below demonstrates using a method defined in the module in order to get the URL of the HTML document opened in the current tab (window in IE6). The method is invoked from an Explorer bar; the resulting value is used to fill in a ListBox on the IE bar.


				'...
				Public Class MyIEBar1
					Inherits AddinExpress.IE.ADXIEBar
				 
					Friend WithEvents ListBox1 As System.Windows.Forms.ListBox
					Friend WithEvents Button1 As System.Windows.Forms.Button
				'...
					Private Sub Button1_Click(ByVal sender As System.Object, _
						ByVal e As System.EventArgs) Handles Button1.Click
						'System.Diagnostics.Debugger.Launch()
						Me.ListBox1.Items.Clear()
						Dim theModule As MyIEAddon1.IEModule = _
							CType(Me.Module, MyIEAddon1.IEModule)
						For i As Integer = 0 To theModule.GetModuleCount - 1
							Dim aModule As MyIEAddon1.IEModule = _
								CType(theModule.GetModuleByIndex(i), MyIEAddon1.IEModule)
							Me.ListBox1.Items.Add(aModule.GetURL())
						Next
					End Sub
				End Class

				'...
				Public Class IEModule
					Inherits AddinExpress.IE.ADXIEModule
				'...
					Friend Function GetURL() As String
						Return Me.HTMLDocument.url
					End Function
				End Class
			  

Messaging

Here we talk about features available for ADXIEModule, ADXIEBarModule, ADXIEBar, ADXIEToolbarModule, and ADXIEToolbar.

The SendMessage, SendMessageToInstance and SendMessageToAll methods provide an asynchronous way to invoke the functionality of the current, specified or of all instances of a module, toolbar, or Explorer bar. This is implemented by sending a WinAPI message to a hidden window that Add-in Express maintains. For that reason, SendMessageToInstance provides the nativeHandle parameter, a proper value for which can be obtained using the NativeWindowHandle property. You can also use the FindNativeWindows method; it allows finding hidden windows that belong to the same main window or process in IE. On the receiver side, you filter incoming messages (there will be a huge lot of non-related window messages!) and invoke the functionality required in the OnSendMessage event.

The code below demonstrates using SendMessageToAll to keep the same state of a checkbox through all instances of an IE bar.


				Friend Class MessageConstants
					'window messages
					Public Shared ReadOnly WM_USER As Integer = 1024
					'custom
					Public Shared ReadOnly WM_CHECKED As Integer = WM_USER + 10000
					Public Shared ReadOnly WM_UNCHECKED As Integer = WM_USER + 10001
				End Class

				'...
				Public Class MyIEBar1
					Inherits AddinExpress.IE.ADXIEBar
				 
					Friend WithEvents CheckBox1 As System.Windows.Forms.CheckBox

					Private IsProgrammaticChange As Boolean = False
				'...
					Private Sub CheckBox1_CheckedChanged( _
						ByVal sender As System.Object, ByVal e As System.EventArgs) _
						Handles CheckBox1.CheckedChanged

						'System.Diagnostics.Debugger.Launch()
						If IsProgrammaticChange Then Exit Sub
						'do my stuff
						If Me.CheckBox1.Checked Then
							Me.SendMessageToAll(MessageConstants.WM_CHECKED, _
								IntPtr.Zero, IntPtr.Zero)
						Else
							Me.SendMessageToAll(MessageConstants.WM_UNCHECKED, _
								IntPtr.Zero, IntPtr.Zero)
						End If
					End Sub

					Private Sub MyIEBar1_OnSendMessage( _
						ByVal e As AddinExpress.IE.ADXIESendMessageEventArgs) _
						Handles MyBase.OnSendMessage
						If e.Message = MessageConstants.WM_CHECKED Then
							IsProgrammaticChange = True
							Me.CheckBox1.Checked = True
							IsProgrammaticChange = False
						ElseIf e.Message = MessageConstants.WM_UNCHECKED Then
							IsProgrammaticChange = True
							Me.CheckBox1.Checked = False
							IsProgrammaticChange = False
						End If
					End Sub
				End Class
			 

Intercepting IE and HTML events, keyboard shortcuts <<

>> Tips on Internet Explorer add-ons