Sunday, February 19, 2006

The Thermometer Of Success

While I am now doing some serious .NET work I am finding myself blogging more about non-programming topics. I subscribe to many RSS feeds, including the aggregation provided by .NET Developers Blog. I read a post yesterday by Andre Cruz about software for monitoring system temperatures.

 I was accustomed to this kind of system monitoring software being provided with the motherboard drivers for all my desktop computers but my laptop did not include anything like this. Looking at the laptops of different brands used by my colleagues this seems to be a common oversight by laptop manufacturers.

Thankfully, the MobileMeter software mentioned in the blog provides the perfect solution. It utilises ACPI to display all the temperatures available from the system which includes processor and hard drive on my laptop and also several others on other laptops. It also shows current processor speed (I was surprised how low it drops on some of my battery power modes) and even the current battery charge or discharge rate in Watts.

The best feature however is that I can not only set the MobileMeter window to be always on top and very transparent, it will also allow all mouse operations on the window to pass through to underlying objects as though the window wasn’t there at all. This is the best system tool I have encountered since Sysinternal’s Process Explorer.


 Friday, February 17, 2006

The ‘Net Is Getting Tighter

This post isn’t about .NET programming directly but it is about the tools I use when programming. I am quite fond of using batch files to execute common maintenance tasks like archiving source files or performing custom builds. However, sometimes the target folders of the actions within these batch files are located on network shares that are not mapped to a network drive. Most batch commands will happily accept UNC paths as arguments on the command line but the command prompt (cmd.exe) does not support setting the current directory to a UNC path.

I don’t like to map network drives for infrequently accesses shares and I especially don’t like mapping network drives to the hidden x$ administrative shares. A common technique is to call NET USE at the beginning of the batch to temporarily map the share until a call to NET USE /DELETE at the end of the batch cleans it up. Even better though, I found some articles on Microsoft support describing easier methods to achieve the same result.

Knowledge Base article 156276 specifies a registry key that can be changed to enable a UNC path as the current directory but this didn’t seem to work all the time for me. Article 317379 describes the more portable method of using the PUSHD and POPD directory stack commands that will automatically map an unused drive letter to the UNC path and remove it when done.

I also discovered recently that Windows XP has a built in VPN Server feature accessible from the Network Connections folder in Control Panel. Run the New Connection Wizard, select “Set up an advanced connection”, and follow the prompts. It’s scary that I am still discovering new features when I have been using Windows XP since release.

 Thursday, February 09, 2006

The Faulty Default

I haven’t blogged in a while. Some of you may have noticed. Some of you may have rejoiced. I have just started a new job this month after leaving my previous job of almost eight years. I have also been trying to find and purchase a new home for my fiancée and myself. My new job is great but my work with Visual Studio 2005 is exposing me to the many interesting glitches in the .NET framework again.

This week’s gem was encountered while developing a base form to inherit from as a template in several other projects. This base form has a ToolStrip with a collection of standard buttons that will be common to all projects. In VS2005, the ToolStrip and other collection based controls have very limited design-time support in visual inheritance situations. The reasons for this and the associated problems deserve an article of their own that I may write about later.

To workaround this I created a series of properties on the base form to allow some of the standard buttons to be hidden using a simple Boolean property accessible through the designer. Initially I coded these properties to simply map to the ToolStripItem.Visible property on each corresponding button. As all the buttons are visible in the base form, these properties are also naturally true. Unless otherwise specified, the designer will assume that Boolean properties default to false and will only serialize the value of the property to the derived form’s designer code if the value varies from the default. This means that whenever the property is changed to False on the derived form, it doesn’t get serialized and it reverts back to True.

To fix this I tried applying the DefaultValueAttribute to each of the appropriate properties in the base form but the designer still insisted on assuming False was the default. I tried the usual: rebuild the solution, restart Visual Studio, search Google Groups and anything else but nothing helped. I found an older project where I had needed to achieve the same default on a Boolean property and investigated that code. It used the DefaultValueAttribute in the same way but this older project actually worked. I spent quite a while trying to track down the differences and eventually managed to simplify the problem.

Basically if the getter of a Boolean property maps to a private member, a function call, a property on any object or whatever and you use the DefaultValueAttribute to set the default to True it works. If the getter maps to a ToolStripItem.Visible property directly or via nested function calls then the DefaultValueAttribute is ignored. ToolStripItem’s also have an Available property which is effectively equal to Visible with some minor conditions and actually calls the Visible property in it’s implementation, however mapping a Boolean property to ToolStripItem.Available works with the DefaultValueAttribute. As far as I can tell, the only potentially relevant difference between the ToolStripItem.Visible and Available properties is that Visible has the LocalizableAttribute set to true.

According to the documentation the LocalizableAttribute determines whether the designer will serialize the value to code or to a resource file. Considering I simply call the property getter and not inherit from it this should not be a problem but it is the only thing that seems to explain my results. I have built a very simply project to reproduce this strange behaviour and you can download it here. I don’t know whether this is a bug or is by design but it sure is irritating and I’m concerned that it may appear in other situations. I’d be interested if anyone can shed any more light on this issue.

 Monday, December 19, 2005

Detached

For the last few weeks I have been frustrated by Outlook 2003 on both my home and work PCs. I send and receive many files as email attachments but this has become increasingly difficult recently.

When receiving multiple attachments in a single message, I usually highlight them all, then drag them from Outlook and drop them into a folder in Explorer, but this now fails without any files appearing in the folder or any error messages popping up. However, using Outlook's Save Attachments menu item still works.

I also find it convenient to right-click a file in Explorer and select Mail Recipient from the Send To menu but when I send the message it produces an error with the text "The operation failed due to network or other communications problems". Using the Insert File menu item in Outlook works fine though.

I decided to Google the error message above but most results from the Microsoft knowledge base and various forums were related to very different problems with Outlook 2000. I tried several variations on the search keywords and eventually found some minor references on a forum to SQL Server 2005 possibly being involved.

I tried including "sql server" in my searches and I still could not find anything on Microsoft's website but I found some better results in Google Groups. Apparently, in some situations, the installation or uninstallation of SQL Server 2005 or the .NET Framework 2.0 can cause the registry entries for OLE32.DLL to become damaged.

I remembered that my problems with Outlook started at about the same time Visual Studio 2005 Professional was installed on both my home and work PCs. I decided to try to repair the registry entries for OLE32.DLL by running "regsvr32 ole32.dll" from the command line as suggested by one of the forums. I am now very pleased that Outlook is behaving as it should.

I'm not positive that Visual Studio was responsible for the OLE problem but it is quite a coincidence and maybe someone else will be able to definitively prove what caused the problem. Until then, this post should help others fix their Outlook if they experience the same symptoms and maybe Microsoft will eventually add an official article to their knowledge base.
 Thursday, December 08, 2005

Loose Thread

It has been a while since my last post because I have been busy. However, I thought I'd better write something about my recent experiences with Visual Studio 2005 and version 2.0 of the .NET Framework. Before I tell you about my first struggle with the new development system, I must say that everything else has been a joy to work with.

I was porting a decent-sized VB.NET 2003 Windows Forms application to .NET 2.0 and updating all the deprecated classes and functions to the new preferred alternatives. There weren't too many problems and I finally cleared all the build errors and warnings. Sadly, not everything was perfect.

Back in VS2003, I had put some effort into making most of the SimpleMAPI functions accessible to my .NET application, mainly for the purpose of opening a new message window with the recipient and subject pre-filled and an attachment ready to go. Not too hard once you know how each Win32 API parameter maps to each .NET type. One annoyance with this SimpleMAPI feature was that showing a new message window was a blocking call that didn't return until the user sent the message or closed the window. I threw together some quick code to spawn a new background thread to make the call and everything was perfect. Until VS2005...

Suddenly, my application, recompiled for .NET 2.0, was complaining about a MAPI logon error. I knew VS2005 had introduced mechanisms to prevent Forms being accessed on other threads so I ruled that out. I also tried different Outlook and Outlook Express configurations. Finally I tried making the call on the main thread and it worked again, but why? I scoured Google Groups for a while and picked up on some obscure references to COM thread apartments.

According to the new documentation, VS2005, by default, creates all new threads with a multithreaded apartment state (MTA). SimpleMAPI (and other COMish APIs) don't like this. Inserting a single line to change to a single-threaded apartment (STA) before starting the new thread solved the logon error problem. I haven't found any official documentation yet but my conclusions would suggest that previous versions of .NET either defaulted to an STA or it was dependant on context.

Hopefully this post will save someone else the same headache I endured.
 Sunday, June 26, 2005

Mind your ODBC

I have planned to post about MYOB's developer support for some time and I was provoked by one of Clarke Scott's recent blogs to do it now. I have been working with a wholesale company to automate the export of data from their MYOB Premier company file to their website on a regular basis. They want their customers to be able to access current pricing and stock levels of all the products they distribute, and they want their customers to be able to view current back orders and recent invoices online.

I have done plenty of work like this before for non-MYOB accounting systems and even MYOB RetailManager (which conveniently uses a Microsoft Access database). Unfortunately, every other MYOB product uses a proprietary database format, which, at the time I started this job, required the customer to pay $259 Australian per company file for an ODBC driver. The ODBC driver was read only and was incompatible with ADO.NET in certain situations. For documentation on the MYOB table structure, some code samples and three months forum access, MYOB Australia expected the developer to cough up around $900. I discovered however that MYOB USA provides table structure documentation (the "Data Dictionary") for free download from their website. A few tables have slightly different names and some tax related items are different but it is mostly identical to the Australian MYOB system. I decided to call MYOB Australia about this situation and their response was basically, "that's the way it is, live with it".

Thankfully, in the last three months, MYOB Australia has improved their support for developers. They now provide the Data Dictionary, sample code, and tools for free download from the website. The ODBC driver has been updated and has hopefully been improved but I have not tested the ADO.NET problems yet. MYOB also have an excellent Developer Partner Program now. For just under $700 per annum you get an ODBC driver with write access, full access to MYOB's email and forum support, and your customers can use the ODBC driver with their company file free.

If you are working with MYOB, or plan to, the partner program is excellent.

 Wednesday, June 15, 2005

Truly Resourceful

While Googling for something quite different, I stumbled across an article on The Code Project by Scott McMaster. The article covers almost everything you would want to know about using the Win32 MessageBoxIndirect API from .NET. Specifically, he discusses the problems with using custom icons considering .NET resources and Win32 resources are completely different. One of Scott's solutions is to create a small unmanaged Visual C++ project purely to host the icons as Win32 resources.

However, Scott's MessageBoxIndirect wrapper exposes most of the gory details of the API and expects the caller to pass in a handle to the C++ resource DLL as an IntPtr. I decided to tidy this process for the wrapper I built and added an IDisposable implementation to my class. This class accepts the path to the DLL as a String and the resource ID as an Int32 and handles the calling of the LoadLibraryEx and FreeLibrary APIs appropriately.

 Tuesday, June 14, 2005

The framework from my point of ListView

In my ongoing war with the Framework, a memorable battle worth documenting is that of the ListView and the BackgroundImage. My current major project consists of several forms that all have a large ListView control in Details view as the prime user interface element. These forms are so similar that I was able to inherit them all from a custom base Form with most of the functionality. Unfortunately, some users don't really pay attention to what is in front of them, so I decided to put a nice big picture as the background of the ListView to make things obvious.

The ListView control has a public BackgroundImage property but it has attributes preventing it from displaying in the Properties window. I tried setting BackgroundImage property via code and discovered that it has no effect. After a great deal of digging with Lutz Roeder's trusty Reflector I discovered that the problem is a combination of the ListView's ControlStyles and the Control's WmEraseBkgnd. To solve this I created a new class to inherit from ListView and catch the WM_ERASEBKGND message in an overridden WndProc. In the WndProc, I change the UserPaint and AllPaintingInWmPaint styles appropriately, pass through to MyBase.WndProc to draw the background image, and finally revert the styles to their previous values. I also overrode the BackgroundImage property's attributes to show it in the Properties window again.

This time the background image shows in the ListView but as soon as an item is added to the list it obscures the image. Reflector showed that ListViewItems have their own BackColor which defaults to the parent ListView's BackColor if not specified. I changed my code to set each ListViewItem's BackColor to Color.Transparent but it didn't help. Further digging in Reflector shows than the framework's ColorTranslator.ToWin32 method discards transparency information. To solve this I catch WM_REFLECTNOTIFY (a special modification of WM_NOTIFY) in my subclass, call MyBase.WndProc to do most of the work, obtain a NMLVCUSTOMDRAW structure from the message's LParam, then use my own transparency-aware ToWin32 method to set the item's background colour correctly.

So now the background image shows and the items don't obscure it but as the selection moves from item to item, the unselected items still appear highlighted. My code never puts more than 50 items in the ListView so I decided to take the easy way out and call Me.Invalidate in my subclass' overridden OnSelectedIndexChanged method. I should find the rectangles for the unselected item(s) and only invalidate those but I will do that later when the effort is justified.

I hoped my custom ListView would be perfect now, and it was for a while, but as soon as it contained enough items to require scrolling I noticed the background image became garbled after each scroll. The ListView does not provide OnScroll methods so I needed to catch the WM_VSCROLL and WM_HSCROLL messages and once again call Me.Invalidate. The background image is always drawn correctly now but the list flickers when scrolling and I can't justify the effort to solve that minor glitch yet.

I decided to setup a PaintBackground event (normally missing from the ListView control) so I can draw dynamic information onto the ListView at runtime and everything is just dandy now. With this much work involved I though it was worth checking if these problems are fixed in .NET 2.0. I downloaded the beta and was pleased to find many new features in the ListView control, including support for the BackgroundImage property. Sadly, testing this feature showed that the people at Microsoft also had problems with the selection highlight not clearing. I lodged a bug report and apparently it has been fixed in time for the official release. I'm glad I don't need to fix the new one myself too; .NET 2.0 is much more complicated under the hood.