email notification from NANT

Setting up a NANT build script to send an email when the script completes is something I have done for several clients. When I search the web I find people are talking about it but there are not a lot of complete examples. So here is my version of how to do it. I hope it saves someone out there some time.

First these were the requirements I set out to achieve.

  • Generate a log file
  • The log file must have the date and time of the build in it's name
  • Send email notification of success or failure.
  • Attach the log file to the email

There were several NANT tasks and properties I used to accomplish this.

  • record  (You will need NAntContrib to use the record task)
    • A task that records the builds output to a file
  • mail
    • Sends an SMTP message
  • nant.onsuccess property
    • The name of a target to be executed when the build succeeds.
  • nant.onfailure property
    • The name of a target to be executed when the build fails.
  • tstamp
    • Sets properties with the current date and time.

The link below contains an example, with comments explaining what each section is doing. Keep in mind the rest of the script has been removed for clarity, therefore all you see is the entries that do what I explained above. See attachment for sample NANT Script.

As an aside you don't have to use the record task from NAntContrib you can just pipe the output from NANT to a file using the logfile command line argument. [nant.exe -l:c:\logfiles\Build.log] This output is perfectly acceptable, the problem is you can't attach it to an email and send it because it's still open and being written to. But it actually contains more information than the log file in my example. You see once you close the log files you stop writing to them. Therefore on a script failure the reason for the error does not get written to the log. I'm still looking for a way to get a complete log file that I can attach to an email and send from within a script. If you know how to do it please let me know.

The Best of both worlds

So how is this for getting the best of both worlds.

Below you see a application using .Net Datawindows. This application took less than an hour to write. I'd like to see you write this app with PowerBuilder or VB.NET in the same time. Since all you see here on my BLOG is a screen shot, let me tell you about some of the features available in this little application.

  • There are four DataWindows. A Freeform, a Graph, labels and a Group By. They are all sharing the same disconnected record set. using a DataWindowControl method called share( ). This means if you change data in the top DW it is immediately reflected in the other Datawindows. Imagine how long creating these different views would take.
  • Thanks to VB.Net I was able to use panels, splitterbars and docking which allowed me to place these on the form and be able to shrink and grow the region they take up on the form. This was never easy in PowerBuilder.
  • Total lines of code: 5

I think the two tools together make great development partners. (I know the app is ugly that is not the point.)

Then if I change some data in the main DW and move the splitter bar over. You see the graph and Nancy's address label is different.


What is a DataWindow?

Since I started Blogging about the .NET DataWindow. I have had some developers ask the following questions:

  • What is so great about a DataWindow?
  • Why do you care if you can use one in your .NET application?
  • Why not just use a DataGrid?

I thought I would try and answer some of these questions for those that have never used Powerbuilder.

First you must realize there are two different objects that both get called a DataWindow. A PowerBuilder developer knows which you are talking about by the context it's used in. They are

  • DataWindow Object - Class which contains the Presentation and SQL to retrieve Data and display it to the user.
  • DataWindow Control - A Control you place on your window that takes a DataWindow Object as one of it's parameters. This object presents the DataWindow Object to the user on your form.
    • Inheritable
    • Sample Methods
      • Retrieve
      • Update
    • Sample Events
      • RowFocusChanged
      • ItemChanged

In the DataWindow editor you define a DataSource, the problem is that DataSource is part of the Datawindow Object. Sybase has to change the .NET Datawindow to take an external DataSource, like a DataSet or Business Object that implements IListSource. I may write something that hooks a DataSet to a DataWindow if I get time. Perhaps Sybase will fly me down to meet with the.NET DataWindow Architects and give them some suggestions. Hint Hint

Back to how it works. Once you have your Data Source defined you move to the presentation side of the DataWindow. Here is the good thing about having the data and presentation together in one object. There is no binding issues. You get a column for each column in the result set. So you paint your Datawindow and please realize this is not just a grid. This can be a graph, labels, grid, tabular or you can make it look just like a for with buttons, group boxes, textboxes and labels.

Once you have a completed DataWindow. You drop a DataWindow Control on your form set the DataObject Property to the name of your DataWindow Object. Then just tell the DataWindow Control what Transaction you are using and call the retrieve method.

So that is a really simple example of how it works. In my next Blog entry I'll tell you why I think they are so good. 

My .NET Datawindow works

I got some help from two very nice gentleman at Sybase who read my Blog and offered to help me get my sample application with the .NET DataWindow working. As it turned out I abandoned the project I made and created a new one and it worked. I have no answer as to why at this point.

So I made a C# windows application and a VB.NET windows application. Each with a Datawindow that gathers data from the employee table of the Northwind Database. The DataWindow I created contains a computed field, and expressions to colour certain rows and columns based on the data retrieved.

I also added a sybase transaction object and three lines of code that I have written in powerscript many many many times.

  1. this.transaction.Connect();
  2. this.dataWindowControl1.SetTransaction(this.transaction);
  3. this.dataWindowControl1.Retrieve();

So check it out. Below you see a Datawindow. With the following expressions. If the country is UK make the whole row grey. If the employee was born before 1960 show their name in red. Autosize the height of the address column. :)

DataWindow .Net

Tonight I installed DataWindow.Net, which is shipping with PowerBuilder 10. It's a set of libraries that let you use PowerBuilder DataWindows in your .Net applications.

I downloaded an evaluation version of DataWindow.Net and the Datawindow Designer. I had to see what it was like. So I installed it, the install said it couldn't find a copy of Visual Studio 2003. So I had to add the Datawindow objects to the VS toolbox myself.

It added a DatawindowControl and DataStore and a Transaction Object.

So I opened the datawindow Designer and created a datawindow. Didn't have much trouble with that, it looked pretty much like PowerBuilder. Then I created a windows application in .Net and dropped a DataWindowControl on the form set the LibraryList property and DataObject property and there was my Datawindow in my .NET form.

I added a button and coded a setTransaction and Retrieve. (DataWindowControl methods to tell the datawindow what Database to connect to and execute the Select command of the Datawindow.)

My application built without error. And the bin directory contained a PBD (PowerBuilders version of a DLL) containing my Datawindow Object, along with some other Sybase DLL's. Unfortunately when I run my little application I get a TypeLoadException “Could not load type Sybase.DataWindow.DatawindowControl“. But I'm tired anyway so I'll try again tomorrow.

I'm going to try and do some more investigation. Mostly just for my own curiosity. There are a lot of things I don't miss about the Datawindow but there are a lot of things I miss a lot.

If anyone else out there is evaluating this new Sybase effort to save the DataWindow I'd be very interested in your findings also.



Is IntelliSense showing you everything?

Well I learned something new today. I can't believe I have been developing in Visual Studio .Net for so long without knowing this one. I'm sure someone showed me at one time or another. If they did I forgot.

A colleague of mine mentioned I should use a particular property on a particular class. When I went to use it, it did not show up in IntelliSense. I mentioned this to my very clever friend, who pointed out the following option to me. Apparently the property I was trying to access via IntelliSense was flagged by the developer as an advanced member.

If you have members in a class that you want to be treated as advanced, use the following attribute;



Productivity tips for the Immediate window

Here is a nice little Immediate window trick I just came across. It's not going to change your life but it might make using the immediate window a little easier.

Have you ever wanted to change the last thing you looked at slightly, perhaps you were looking at the number of rows in the order Table.


and now you want to see the number of rows in the OrderDetail Table. Just highlight the command you just entered, and start changing it, a new command is created automatically.

This this nice when you are checking all the items in a collection by just editing the index.

  • ?OrderData.OrderDetails(0).itemarray
  • ?OrderData.OrderDetails(1).itemarray

I know, your thinking you can just hit the up arrow and the last command is yours for the changing. This is just another little trick for duplicating and altering the last command at the same time.

The Imagine Cup Kids

At VSLive ObjectSharp hosted a Limo Bus for speakers and special guests. Our special guests included the Canadian Imagine Cup winners, who just recently went to Brazil for the big competition.

Sadly they did not win, but what a ride they had. If you want to read about their travels here are their Blogs. these are also under links on by Blog page.

Dear Imagine Cup Kids,

What a wonderful accomplishment to get as far as you did. We at Objectsharp are very proud of you all. You're a great group and worked very hard to get where you did.  You will all go far in this industry, of that I'm sure.

Sounds like you are having a blast in Brazil. Now it's time to relax and have fun. You have earned it.

Spam Spam Spam Spam Spam Spam...

I am sick and tired of Spam and I know I'm not alone on this. I don't just mean the email kind of spam. Spam comes in many forms. It's far from new, all that's happened is the spammer has new tools.

These are the Spams I hate (in order):

  1. email Spam
  2. Phone calls at dinner
  3. The crap you get inside every statement you receive
  4. Junk mail
  5. The stuff that falls out of magazines

Although 1 and 2 don't kill tress, I find them the most intrusive. I hate the others but not because of the intrusion, I just toss them aside without even looking at them. Which means valuable resources have been wasted. If anyone reading this sends me junk mail. STOP IT. You are wasting your time, money and our planets resources. I will never buy anything from you because of this, so just stop.

At least with the phone calls there is someone to take it out on. You can yell at the poor telemarketer and feel a bit better. Also there are two ways to get ride of the dinner time callers. Call privacy is one, unfortunately this costs money. You can also combat this yourself, by asking to be taken off their list. By law if you ask the telemarketer to remove you from their list they must comply. And it always stops their spiel when I say it.

So what about the worst kind of Spam. The tons of email we get in our email box each day. You can't even reply to these bastards to yell at them. The current thinking in the industry is, creating filters is just not worth it. There is just no way to weed it all out. The best defense would be to make it not worth while doing. If it's not cost effective the spammers will stop. One way to do this is to charge postage on email. If we each had to pay a penny to send an email. It wouldn't cost much and if it meant that the spammer sending millions would stop because it cost to much, I think it would be worth it.

Cynthia Dwork of Microsoft has another idea. Charge for sending email but not with money with Processing power.

What Dwork proposes is to slow down the process by having the sending computer solve a mathematical puzzle created from the details of the message itself. Messages sent at different times and to different recipients would generate a different puzzle. This way a message sent to many different people would have to solve a different puzzle for each one. Thereby slowing down the machine sending emails. She also tosses in a way to combat the fact that processing power doubles every 18 months, by tossing in a variable that ensures each message takes 10 seconds to send. And will increase this value as machines get faster. So for you and I to send a few messages, our machine works away on a problem as it sends each message, so what. For a spammer they would require more hardware and processing power to send out the same volume of emails they send now. Therefore it might not be worth the effort anymore.

I'm behind you on this one Cynthia. If there is anything I can do to help get this through you can count on my support.


Getting the DataRow that is bound to an Infragistics UltraGridRow

Here is one of those posts I make because I keep forgetting how to do it. Now that I have posted it I won't forget. Perhaps someone else will be searching for this little code snipit and I will have helped them out. :)

If you have an Infragistics Grid bound to a Dataset, and you want to get the DataRow that the UltraGridRow references. Here is what you do.

Cast the UltraGridRow's ListObject property to a DataRowView and get the DataRow from that.

I'll do the code sample in VB this time.

Dim GridRow As UltraGridRow  'Let's assume you have the GridRow perhaps passed in an event argument
Dim OrderRow As

OrderRow = CTypeCType
GridRow.ListObject, DataRowView  ).row , Myapp.Orders.OrderData.OrderRow  )

This will return Null if it's an UltraGridGroupByRow.