IE add-in development in VB.NET, C#:
tabbing, windowing and threading, instancing
How your add-on loads into IE
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:
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
To access the IE module form the IE bar (IE toolbar), use ADXIEBar.Module (ADXIEToolbar.Module).
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:
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.
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.