Monday, July 28, 2008
Whenever I run into a weird bug or workaround I like to do a short post about it. Doing so helps the community and it is also self-serving in case I run into it a year or so later after I've long forgot about it. Today is one of those times.
My latest disk batch came for my MSDN subscription and I decided to upgrade Expression Blend and Web to version 2. Granted I haven't really used these tools too much, but I plan on doing so in the future. The installation of Blend went without a hitch, but Web failed indicating that I was out of disk space. I have about 40 GB free so I know that's not the issue.
A quick Web search on http://search.live.com (I don't like Google) led me to a forum post somewhere that explained that the problem seemed to be with Office. I wasn't going to go through that today, so I decided that perhaps it was something about the Office installation. The only thing that stood out to me was an add-in for saving to a PDF document. (My company is in love with the PDF format for same reason.)
Removing that would be easy enough, so I uninstalled it and tried the Expression Web installation again. Sure enough the installation completed without a problem. I then reinstalled the save to PDF add-in for Office and I was on my way again.
Tuesday, July 15, 2008
Today has been one of those days to hate Microsoft Access. My company has dozens of Microsoft Access applications in the enterprise that do everything under the sun. Eventually those will be replaced by me, but in the meantime I have to deal with them. Today was one of those days. In fact today I had two circumstances to reinforce those feelings.
The first came when I had to correct an ordering of results on a form that wasn't matching a report. The report was deemed correct, so I needed to view the design of its query to see how it was different. Immediately, I'm greeted by the warning that the designer cannot represent the join. (Not unexpected since the query is complex.) I click okay, get the information I want, close the query, fix the form ordering, and go on my merry way. That is until the email later.
The email with the attached report showing 18 pages for what should be two was the problem. What happened? Microsoft Access decided to fix the problem of its own volition. And not only that. Microsoft Access decided to save the query definition without a peep, prompt, or otherwise. And to make things worse, it did the same thing when I imported the query from a shadow copy and every other method I could think of. Eventually I had to manually change it. (I promptly made a backup that I can plop in place if needed; hopefully, Microsoft Access didn't silently fix my backup.)
The second was in relation to an Access project that is being put in place as I push the current data from Microsoft Access into Microsoft SQL Server. One of the constructs is a report with a simple subreport. The subreport represents a one-to-many relationship with a simple list of items corresponding to the main report. This shoud have been easy. My queries were tested in Management Studio and they run individually in the reports. However, when the subreport was added to the main report and run I got the error "Syntax error or access denied". Thanks for the information Microsoft. Obviously, the problem is in the subreport, because the main report shows.
Well, I start simplifying the reports and eventually get down to one with no joins in each. I still got the error. The problem? The SQL statement on my subreport is terminated with a semi-colon (by ANSI standards interestingly). I guess that's a syntax error, but in of itself it's not -- just when combined with another SQL statement through the main report.
So at the end of the day, Microsoft decided to change and save a query I didn't change myself, and inconsistently decided not to change the SQL statement that it could have easily changed on the subreport. My choice is for it not to change anything without me explicitly telling it, but if anything I would like some consistency.
Thursday, June 26, 2008
Here's a little method I came up with recently to test the execution of events. You may not ever need to test to see that an event fires, because you have some other state that you can check. However, that is not always the case. Here is how I approached it using the unit testing provided with the developer's team editon of Visual Studio.
The first thing I did was add a wait handle object inside the definition of my unit test class. Specifically, I chose a ManualResetEvent for the most control, but other types of wait handles may work fine.
private static ManualResetEvent _waitHandle;
The next step was to add code for the class initialize and cleanup methods. I need to create my wait handle and after I finish running my tests I want to make sure that I release any resources tied-up by the wait handle. Additionally, after a test is run I want to make sure that the wait handle is in an unsignalled state. Therefore, I need to provide a test cleanup method.
//Use ClassInitialize to run code before running the first test
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
_waitHandle = new ManualResetEvent(false);
}
//Use ClassCleanup to run code after all tests in a class have run
[ClassCleanup()]
public static void MyClassCleanup()
{
if (_waitHandle != null)
_waitHandle.Close();
}
//Use TestCleanup to run code after each test has run
[TestCleanup()]
public void MyTestCleanup()
{
_waitHandle.Reset();
}
That's pretty much it except for the logic in the code itself. In that code, you create your event handler, run the action that will trigger the event, and then wait for the ManualResetEvent to be signalled. It is advisable to provide an arbitrary yet realistic value for a timeout so that your test doesn't run forever if the event isn't fired.
[TestMethod()]
public void EventExecutedTest()
{
MyObject target = new MyObject();
// TODO: add additional declarations as needed for test
target.EventExecuted += (sender, e) => _waitHandle.Set();
// TODO: perform actions that need to trigger the event
bool result = _waitHandle.WaitOne(3000, true);
Assert.IsTrue(result);
}
As you can see there's not too much to it. This is a simple mechanism to unit tests events when it makes sense.
Friday, May 23, 2008
I just recently installed the new version of the Smart Client Software Factory (SCSF) and wanted to utilize the Composite UI Application Block (CAB) support within my Infragistics components with it within Visual Studio 2008. Luckily, the process is easy and these steps are all you have to do:
- Open Visual Studio 2008
- Open the Infragistics.CompositeUI.WinForms solution and allow the conversion
- Add references to the following Microsoft libraries from the SCSF installation:
- Microsoft.Practices.CompositeUI.dll
- Microsoft.Practices.CompositeUI.Winforms.dll
- Microsoft.Practices.ObjectBuilder.dll
- Specify a key file for signing in the project settings if desired
- Build the solution
The Infragistics composite UI solution is located in the C:\Documents and Settings\All Users\Shared Documents\Infragistics\NetAdvantage for .NET 2008 Vol. 1 CLR 2.0\CAB Extensibility Kit folder (folder may vary based on the version of the Infragistics controls). You must have at least 2008 Vol 1. controls.
Saturday, April 26, 2008
Probably all of us know now that you shouldn't catch general exceptions (CA1031). There are practical reasons for this rule such as the fact that you really cannot handle an OutOfMemoryException or StackOverflowException for example. Here is an example of catching a general exception for those that might not know what I'm talking about.
try
{
}
catch (Exception ex)
{
}
There are also more intrinsic design reasons why you shouldn't catch a general exception. And if there is no other reason, if you're catching a general exception can you say you really know your code? And if you don't know your code maybe it's time to step back and put a little more thought into what you're doing.
But, wait, what if you know your code but the code your try block is nesting isn't known -- such as calling an abstract member within a base class? Still, I say no general exceptions. Its better to let the application come tumbling down then the possible side effects of catching and either handling or swallowing an exception that you really don't know how to properly handle. Okay you say, what if its imperative that the application stay alive despite the possibility of ill-behaving code in a concrete class? Fine you got me there, its time to catch the general exception -- with the caveat that you still need to properly handle the exception.
How do you handle a general exception, because by its definition it could be virtually anything? You really cannot, but you can handle quite a bit and remember that by virtue of this application that must have high reliability (be up all of the time). When you have an application like that you probably have some type of service managing the class. Therefore, what I do is add an event to my class called UnhandledException.
The service class can subscribe to that event and choose to handle it or not via a Handled property on a concrete implementation of the EventArgs class. With that in mind you just need to address your initial error handler and make sure that anything that isn't handled by the service class is then re-thrown which will and should take down the whole shebang.
try
{
// some process that can raise an unknown exception
}
catch (Exception ex)
{
ExceptionEventArgs args = new ExceptionEventArgs(ex);
OnUnhandledException(args);
if (!args.Handled)
throw;
}
As you can see from this implementation we leave it up to the service class to determine what to do with the exception. You still may have tasks to do with specific exceptions and in theses cases you should have blocks for those specific exceptions. However, this cleanly delegates the work to the service allowing it to make the determination of what to do -- which may include tearing down and recycling the instance that raised the exception.
Thursday, March 20, 2008
You really start to appreciate the little things you have from modern languages and development environments. I use Visual Studio 2008 now with all of the Intellisense goodness, tools, designers, et al. But, like anything you start to take it for granted until in this case you're forced to work on a legacy, proprietary scripting language like the one in the data collection software that we use.
The program is called VisiBar and it's primarily used for barcoding. This application uses it's own language so to speak which is made up of a series of constructs called verbs. Verbs such as STORE, RSOPEN, IF, and GOTO (sorry in this language you have no choice) are tied together to create a distinct function. Function is really a misnomer and it should be called a script. This language, and I really use the term loosely, makes certain operations with data really easy; for example, by using RSPOPULATE you can in one step create an array for all of the fields in a record set. Unfortunately, it also makes some things very difficult. Case in point, the IF verb. Every language I know (besides this one) uses IF to perform a block of code conditionally. With VisiBar, IF is really a conditional GOTO. So, if some statement is true then goto a specific label. There's also no concept of boolean logic in your variables, so you're forced to assign "Y" and "N" values to variables and if you want to say something like if x > 0 and y > 0 you need to do something like this:
STORE xGTZ SWAP(LEFT(x, 1), "Y", 0, "N", "-", "N")
STORE yGTZ SWAP(LEFT(y, 1), "Y", 0, "N", "-", "N")
STORE xyGTZ xGTZ & yGTZ
IF xyGTZ IS EQUAL TO "YY" XYOKAY
In the above script I check for the negative sign and also compare to see if the value is "0". I assign "Y" as the default if none of my other checks turn out (which is what I want). The variables that store these initial "Y" or "N" values are xGTZ and yGTZ. xyGTZ is then formed by concatenating the prior to variables. I then use an IF verb to see if both values are true by looking for "YY", and if that is the case I can goto the label XYOKAY.
That's a lot of work for a simple comparison. But, that's what I'm working on now. It definitely makes me appreciate Visual Studio, C#, etc. and it also makes me want to push up the future project replace this old software.
Monday, March 10, 2008
I received an email this morning from my manager saying that I need to look at this one utility, because it's showing a high CPU utilization and preventing another application from running. The utility runs on a Terminal Server so the problem is exaserbated by multiple users in different sessions.
Keep in mind that this utility has been in production for several months and there have been zero bug reports. But, sure enough using the information I was able to duplicate the problem and based on the details of the problem I knew what function was causing the problem and I quickly saw the problem:
// wait for a signal from AddRequest, ClearError, or the Stop method
if (_disposed)
_waitHandle.WaitOne(timeout, false);
A half a day of work lost, because I messed up the logic and should have said, "if (!_disposed)". Oh well, it seems that no matter how well you think you test the code that there's always the possibility of something funny like this that you don't look for. I feel comfortable saying that the utilty is still well tested since its not a trivial utility and I've only had one bug report. But, I still feel like slapping myself for that funny little logic error.
Anyone else do these funny little logic errors?
Wednesday, January 16, 2008
Attributes are elements of metadata that you can attach to entities such as assemblies, classes, methods, properties, et al. The .NET Framework makes use of many attributes that allow you to affect the behavior of the compiler or how the Framework uses an entity. For example, you can assign attributes to properties of a control object that allow a friendly name and description to be shown in the property browser in Visual Studio.
At some point, you'll come into a situation where you'll want to use a custom attribute. Defining and using attributes are very easy and in this article I will provide a very simple setup using a few attributes defined at the class level. Defining and using attributes at other levels vary only slightly and I'll make note when there is a difference.
Read the rest of the article