Wednesday, September 16, 2009

Listening to HTML Events from BHO

Creating a plug-in for Internet Explorer was an interesting proposition. After years of .NET development, the first point I investigated was how to write my add-in using C#. I was surprised to find no official support from Microsoft for .NET development of IE add-ins. Thanks to COM Interop, however, it's still possible. More so, information and sample code abound for making it happen.

A number of Code Project articles have been written about how to setup your add-in project using COM Interop. But I wanted to get straight to writing C# code and seeing its effects within IE. In other words, I wanted an existing library that did the COM Interop stuff for me, and which I could simply reference from my project. Two such solutions exist: one commercial and one open source. The commercial one is Add-in Express for IE. I was always suspicious of their tool since a free trial isn't available, so I never shelled out the $149 (USD). The free open-source alternative known as SpicIE, however, is good enough.

My plug-in needs to be kept in sync with whatever text is currently selected within the browser window. Listening to HTML events raised on the browser's current page is necessary in order to do this. The SpicIE Contrib companion website includes a bare bones sample showing how to do this. This sample, however, hooks up the event handlers only after the entire page has loaded. I was looking for something a bit more interactive than that. In other words, I want to hook up the event handlers ideally as soon as the user is able to interact with page elements. This turned out to be a non-trivial task.

In response to the above problem, I developed a reusable class called HtmlPageEventManager whose purpose is to subscribe to a given list of HTML events for each new webpage that's opened. A goal is to attach handlers as soon as the user can begin interacting with page elements, even before the page load is complete. Using this class is simple -- just call the constructor:
var evts = new List<HtmlEvent>() {
HtmlEvent.onclick, HtmlEvent.ondblclick, HtmlEvent.onkeydown,
HtmlEvent.onselectstart, HtmlEvent.onselectionchange,
HtmlEvent.onfocusin
};
new HtmlPageEventManager( this, evts, this.HtmlEventHandler );
You can download my solution here to try it out. And feel free to use it in your own projects if you find it useful.

I encountered some issues while creating HtmlPageEventManager.
  • As mentioned above, I want to add the HTML event handlers as soon as possible, i.e. even before the page load is complete. Doing so in the NavigateComplete handler seemed to work great, until I tested opening a hyperlink in a new tab or window. The new tab or window often times didn't have the HTML event handlers functioning correctly. I could see from debugging that IHTMLDocument3.attachEvent was indeed called on the correct document or element instance and returned 'true' indicating handlers were attached, but the handlers were thereafter never invoked. It seemed that the document/element wasn't "ready" to have events attached, although that didn't concur with the 'true' return value.
    Via the debugger, I discovered that from within OnNavigateComplete, I can detect the problem situation by looking at the IWebBrowser2.Busy property, and adjust my strategy concerning when to add the handlers. So now the registrations occur in OnNavigateComplete only when browser.Busy is true. And if they haven't been registered by the time OnDocumentComplete is reached, then they get registered there instead.

  • When you "Refresh" a webpage, the event registrations were always being lost. That is, they weren't being reconnected on the new page elements. I found out that the NavigateComplete and DocumentComplete handles aren’t called on a "Refresh". When I tested it earlier, it seemed like they were called. But what I observed must have been those two handlers called for frames within the main document – not the main document itself. I overcame this issue by subscribing to the BHO's DownloadComplete event, where I again call my "RegisterEventHandlers" helper method that attaches the HTML event handlers. I incorporated the idea from this code of a "normalPageLoad" member variable to conditionally call RegisterEventHandlers.

Wednesday, July 1, 2009

DragDropManager for WPF TreeViews and ListViews

Update: Please note that the download for this project has been moved here.

My new drag-and-drop manager for WPF, which handles both TreeViews and ListViews, is ready for download. Here's a list of features it includes:
  • Supports drag-drop operations among any number of TreeViews and/or ListViews.
  • Shows drag adorner while dragging among different TreeViews/ListViews.
  • Explicitly supports two types of drop events: ProcessMove for moving an item, and ProcessLink for linking one (see demo).
  • User controls where, relative to the item under the drag cursor, the item will be dropped: above, below, or (for TreeViews) within.
  • Spiffy animations for grabs, drops, and cancelled operations.
  • Auto-expand item if hovering over it during drag operation.
  • Auto-scroll TreeView or ListView if near top or bottom edge.
  • Works on the standard TreeView and ListView controls. No third-party TreeView or ListView control is required.
  • Not dependent upon a particular presentation-model (aka ViewModel) implementation. The pattern isn't even necessary in order to use the DragDropManager.

Demonstration Application

The demo consists of a TreeView that holds categories, and a ListView populated with the selected category's items. These two controls together have a total of three types of drag-drop operations setup on them.
  1. Move a category within the TreeView
  2. Move an item within the ListView
  3. Link an item with another category
The demo is available for download here. The drag-and-drop setup code in the demo looks like this:

dragMgr.AddControl<Category>( categoriesTreeView );
dragMgr.AddControl<Item>( itemsListView );
dragMgr.AddHandler<Category>( categoriesTreeView, dragMgr_ProcessMove );
dragMgr.AddHandler<Item>( itemsListView, dragMgr_ProcessMove );
dragMgr.AddHandler<Item, Category>( categoriesTreeView, dragMgr_ProcessLink );
dragMgr.GetLinkEffect += dragMgr_GetLinkEffect;

The first two lines indicate which controls will have their dragging managed. The following three lines add the event handlers whose job is to actually move or link the domain-model objects. The last line adds the custom link animations you will see in the demo.

The body of your ProcessMove and ProcessLink event handlers will depend on how you've architected your presentation-model. The demo app doesn't even use one, so the event handlers simply look like this:

void dragMgr_ProcessMove( object sender, MoveArgs<Category> moveArgs )
{
 moveArgs.oldPosition.parent.SubCategories.Remove( moveArgs.itemToMove );
 moveArgs.newPosition.parent.SubCategories.Insert(
  moveArgs.newPosition.childIndex, moveArgs.itemToMove );
}

void dragMgr_ProcessMove( object sender, FlatMoveArgs<Item> moveArgs )
{
 var category = (Category)categoriesTreeView.SelectedItem;
 category.Items.RemoveAt( moveArgs.oldIndex );
 category.Items.Insert( moveArgs.newIndex, moveArgs.itemToMove );
}

void dragMgr_ProcessLink( object sender, LinkArgs<Item, Category> e )
{
 e.TargetLoc.Items.Add( e.ItemToLink );
}

Obfuscation for .NET

Update (1 November 2012):
I recently tried upgrading to the latest Smart Assembly version (now owned by Red Gate) while in the process of upgrading my project to .NET Framework 4.5. The latest Smart Assembly version seemed to work okay at first, but then I discovered a bug that caused it to crash. I sent a bug report to Red Gate, along with a repro, then waited and bugged them until I finally received a reply that said: "I'm hopeful that a release may be available with a couple of weeks, however there's no full time development team currently working on Smartassembly; the majority are deployed on other tools." This was especially infuriating since I had purchased "1 year Support & Upgrades". What kind of support is this? None at all, I think. My conclusion is that development on Smart Assembly is stagnant. Therefore, it's best to consider the product dead.

Original Post:
I recently had to fulfill my need for an obfuscation utility for .NET. More specifically, I needed a solution that works with WPF. I learned that WPF poses a particular challenge concerning obfuscation, since the BAML format has not been made public by Microsoft.

My first attempt was using Dotfuscator Community Edition which comes bundled with Visual Studio. After spending a significant amount of time picking certain properties and methods to exclude, I still couldn't get the obfuscated version of my app to run. So I finally gave up and instead turned to Google to help me find an alternative tool.

The only tool I found, with specific mention of WPF support, was {smartassembly}. As long as I exclude those types and (attached) properties used in XAML, this tool works like a charm.

{smartassembly} projects are stored in an XML format, so you can edit them using either your favorite text editor or {smartassembly}'s designated user-interface. The UI follows a rather interesting concept. It's a gigantic vertically scrollable area, consisting of a collapsible pane for each settings group. It reminds me a lot of a word-processor. This may seem strange at first, but after a couple hours of usage, you'll likely see the design concept's merit points.

{smartassembly} has security and optimization features in addition to simple obfuscation. One of particular interest is error reporting. When your program crashes, error reports can be collected by a server and made available for your viewing. That feature, however, is only available in the Professional and Enterprise editions. The Standard edition will run you 500 USD, and the other editions move up from there. For more information, see the {smartassembly} website.

Wednesday, April 22, 2009

Non-Intrusive Tree & Graph Types

I just submitted a second article to Code Project. This one is called Non-Intrusive Tree & Graph Types. It's an original subject for me, totally different from my first CP article, so I don't know what to expect. I'm looking forward to see what feedback I'll be getting.

Saturday, January 31, 2009

Drag-and-Drop in a WPF TreeView


Update: Get the latest version of my DragDropManger by clicking here.

When it came to adding drag-and-drop support to my application, the unfortunate dearth of an easy-to-use solution for WPF TreeViews became apparent. The closest thing available was drag-and-drop support for ListViews provided by our beloved WPF guru, Josh Smith. So, with Josh's blessing, I set out to adapt his ListViewDragDropManager into a TreeViewDragDropManager. It was a lengthy learning process, but I'm happy to say that the port is complete and a robust solution for TreeView DnD is now available. To demonstrate this functionality, the download contains a modified version of "Demo 2" from my Code Project article.

Although many of the private helpers and event handlers in my port differ significantly from the original, the public interface remains nearly identical. So you can read Josh's article, the "Using the code" section in particular, to get started. But do note the differences. First, my class takes two type parameters (instead of one), which you can best understand in the context of my Code Project article. Second, whereas Josh's ProcessDrop event is optional, my class requires you to subscribe in order to actually move a dropped item.

The drag-and-drop setup code in the demo look like this:
var m = new TreeViewDragDropManager<SampleTreeNode, SampleTreeNodeView>( sampleTreeView );
m.ProcessDrop += delegate( object sender, ProcessDropEventArgs<SampleTreeNode> e ) {
e.DroppedItem.ParentNode = null; //move the selected item
e.NewParent.SubNodes.Insert( e.NewIndex, e.DroppedItem );
};