System.Transactions and legacy code

System.Transaction is the latest incarnation of transactions from Microsoft.  It first showed up on the scene with the release of .NET Framework 2.0.  Today, however, was my first interaction with it.  As with most things I do, I happened to be trying to do something that apparently no one else in the world as done (okay, probably not true, but when I ran into problems I sure didn’t find much help out there).

My goal was to create some WCF services that would expose some legacy C++ functionality.  The C++ functionality was doing updates to some database tables using ADO (the old ADO, not the new ADO.NET).  My approach to doing this seemed quite straight-forward to me so I naturally figured it wouldn’t take me long.  I was going to create a new C++ library containing managed code that would turn around and call the legacy functions that are exported from another C++ library.  My new C++ library could be compiled into a nice .NET assembly that I would reference from the C# WCF library.  The WCF service in the C# library would just call the "wrapper" class in the new C++ library, which in turn would call the legacy function.  Piece o’ cake, right?

Getting things built and compiled was easy.  Getting things to work, however, was not.  My issue was that the database updates that occurred within the legacy code did not occur within the WCF transaction that was created.  So, if I had to rollback the transaction I would be screwed because the legacy updates wouldn’t get rolled back. 

I tried several different approaches.  I even went down the path of creating a ServicedComponent as my "wrapper" and using EnterpriseServices.  It was that path that led me to the answer (or at least one answer that seems to work).  The approach I ended up with is to put a "using (TransactionScope ….)" statement in my WCF operation that wraps the call to the C++ wrapper.  The not-so-obvious trick, however, is that the TransactionScope constructor needs to be the one that allows me to specify EnterpriseServicesInteropOption = Full, like this:

using (TransactionScope s = new TransactionScope(Transaction.Current, new TimeSpan(0, 2, 0),
    EnterpriseServicesInteropOption.Full))  {
// call the legacy component
s.Complete();
}

This made things work like a charm and a did NOT need to have the wrapper actually be a ServiceComponent.

.NET WinForms App - System Tray

I’ve been spending most of my time that past year or so blogging about topics related to web site development. I’m going to switch things up a bit here and talk about my current activity, which is building a .NET WinForm app. There are some interesting things about it. For starters, it’s purpose is to periodically retrieve/scrape some information from a secure web site for the user. This eliminates the need for the user to log into the site during the day to check there statistics. Since I want the app to be started up once and they stay running on the computer, I decided to have the app minimize to the system tray and display alerts (balloons) as things change.

I originally was thinking of using HttpWebRequest and similar .NET classes, but couldn’t get things to work. The complexity is with the web site’s login page and I couldn’t figure out how to correctly post and navigate, so I decided to take a different route.  I’ve added a WebBrowser control to my main WinForm. I’m making it non-visible since there’s no reason for the user to see it. The nice thing about it is that I can use the Navigate method to browse to the correct URLs, and then  use the Document property to get the page’s HTML. It’s pretty easy to find the various HTML elements, set values into fields, and invoke methods.  It’s also easy to extract information.

Here’s a simple example:

HtmlDocument doc = webBrowser1.Document;
if (doc != null)
{
    HtmlElement userId = doc.GetElementById("userId");
    HtmlElement passWord = doc.GetElementById("passWord");
    userId.SetAttribute("value", "myUserId");
    passWord.SetAttribute("value", "myPassword");

    HtmlElement btn = doc.GetElementById("loginBtn");

    btn.InvokeMember("Click");
}

From this example you can see how easy to work with the web page. One thing to understand is that you need to implement the DocumentCompleted event for the web browser control, and that is where you put your code to parse the document. That’s the best way to make sure you don’t try to access the document before it is completely loaded up.

As I mentioned, I want the app to minimize to the system tray. After a little research I found out how easy that is too.  In .NET there is a new class- NotifyIcon - that makes it very easy to have the app icon appear in the system tray and display "balloon tips".

Create the NotifyIcon is simple.  Add a member to your Winform class and the construct it in your page constructor or load.

private NotifyIcon _ni;

public Form1()
{
    InitializeComponent();
    _ni = new NotifyIcon();
    _ni.Icon = new Icon(GetType(), "TrayIcon.ico");
    _ni.Visible = true;
    _ni.Text = "Cool Tool";
    ContextMenu mnu = new ContextMenu();
    mnu.MenuItems.Add(new MenuItem("Options"));
    mnu.MenuItems.Add(new MenuItem("About"));
    _ni.ContextMenu = mnu;
}

Then, at the appropriate point in your code you can display balloons:

_ni.ShowBalloonTip(2000, "Status", "Some helpful message", ToolTipIcon.None);

Next Page »