Tuesday, August 15, 2006

Components need good ISite

Accessing a Form from inside a Component is surprisingly convoluted in the .NET Framework. I created a new class that inherits from Component that would provide extra functionality to any Form I dropped it on. Ideally, it would search it's parent Form for various types of controls and add handlers to certain control events to provide special behaviour. Unfortunately, neither the Component.Site or .Container properties provide access to the parent Form or ContainerControl.

Some of the components included with the Framework are interacting with their parents so I know it's possible. I dropped a ErrorProvider onto a new Form and checked the designer code to see how it was instantiated. I then opened up Reflector, headed directly for the ErrorProvider component and followed this rather bizarre code path:

  1. The designer code creates a private member "components" initialised as a new ComponentModel.Container.  
  2. It then creates the ErrorProvider passing the new Container to its constructor.  
  3. The ErrorProvider constructor simply calls the Container.Add method passing itself.  
  4. The Container.Add method (the trick bit), after extensive validation and array sizing, creates a new ComponentModel.Site and assigns it to the (ErrorProvider) component's Site property.  
  5. The ErrorProvider.Site property stores a reference to the incoming Site then proceeds to request a ComponentModel.Design.IDesignerHost via a call to Site.GetService.  
  6. The ErrorProvider then retrieves the IDesignerHost.RootComponent property, casts it to a ContainerControl and assigns the value to it's own public ContainerControl property.  
  7. The designer then serializes the value of this property to the designer code of the parent Form as "Me.ErrorProvider1.ContainerControl = Me".

The ErrorProvider, and probably many other components included with the Framework, use the behaviour of the designer's code generator to ensure it will have access to it's container at run-time. This feels tightly-coupled yet fragile all at the same time with these odd interdependencies.

Sadly, a Component cannot find trace its roots any other way, so my custom component was forced to borrow this ugly technique. Like the ErrorProvider, my component too must be instantiated differently if created programmatically instead of in the Visual Studio designer.

At least now I, and hopefully you, understand how the Component, Site, and Container classes interact with each other and if you want to access the Form from your own Component, know you can.

 Sunday, August 06, 2006

Adding New

I've spent too much time in Reflector again these past weeks. One of the instigators was the BindingSource AddingNew event. If you happen to handle this event and assign your own object to e.NewObject property, don't expect the Position on the BindingSource to refer to the new item as it normally would.
 
The BindingSource explicitly checks to see if you have set e.NewObject yourself and if you have, it doesn't bother to update the Position to refer to your new item at the end of the list. You will need to set the Position property yourself too.
 Wednesday, May 03, 2006

Cold Cup(Of T)

I often wonder exactly which collection interface I should implement in a new collection class or which I should use as a method parameter. I like to choose the highest level interface that will provide access to the properties and methods I need. Instead of following the links through MSDN Library everytime I quickly created a map in Visio so I can see all the relationships between the standard framework collection interfaces and classes, including the new generic items. It probably isn't complete but I think it is good guide for getting information at a glance.
 
It was interesting to discover the significant differences between the generic and non-generic ICollection interfaces. The original non-generic ICollection provides enumeration support and a Count property. The new generic ICollection(Of T) goes further and includes support for adding and removing items, much more like IList than ICollection. It doesn't even inherit from ICollection which unfortunately means that while most of the standard implementation classes of ICollection(Of T) also implement ICollection, I can't safely pass ICollection(Of T) objects to AddRange methods or common collection constructors.

Deblector arrives

Felice Pollano has just released the first version of the new Deblector addin for debugging within Reflector. It's only alpha at the moment and has some obvious bugs but it is very promising. In the past I have looked at having a Virtual PC with a hacked rebuild of the core framework with debugging enabled. Now I can step into framework code just as easily within the familar Reflector interface. I don't have much experience with IL so I was a little confused at first when multiple lines of IL seemed to execute at once but I quickly realised they all applied to method call setup. I suspect, if possible, we may even be able step through the framework viewed in our chosen language. Keep an eye on this one.
 Monday, May 01, 2006

Role your own

I discovered an annoying limitation in the SQL Server Management Studio today. The GUI does not offer an option to add a Database Role as a member of another Database Role. In my case I wanted to add my "bigAppAdmins" role to the "bigAppUsers" role so all administrators are implicitly users also. Unfortunately I had to resort to the sp_addrolemember stored procedure to achieve this. If I didn't already know that roles could be members of other roles I might have been convinced that SQL Server didn't support it.
 Monday, April 24, 2006

Captain's Log, Stardate NULL

Today I discovered a need for a data-bound DateTimePicker to support the display and input of NULL values. I remembered reading a blog a short while ago about someone else who had already developed a solution.
 
Google helpfully pointed me to Alexander Shirshov's original solution that I had read before. However I also spotted Claudio Grazioli's solution on Code Project which apparently correct some bugs in Alexander's implementation. I downloaded the source, gave it a try and it works very neatly.
 
If anyone would like the source for the NullableDateTimePicker in VB.NET instead of C# just leave a comment.
 Sunday, April 23, 2006

Request to dock

One of the nice parts of working with the Visual Studio 2005 IDE is the very well implemented window docking system. It now looks like there is a library available to achieve the same results in our own applications:

Deblector

Lutz Roeder... you're the bomb. I've been using .NET Reflector for quite some time now and I love every bit of it. If you aren't using it yet, go get it. However, there is one thing I've always wanted to do with Reflector, use it to debug code. As I recently discovered on Mike Stall's excellent debugging blog someone is working on integrating debugging into Reflector. That someone is Felice Pollano and hopefully Deblector will soon become one of my primary development tools too.
 Saturday, April 22, 2006

Mapping the web

Discovered recently that Windows XP has really good support for WebDAV. This command will map an unused drive letter to a folder shared via HTTP:

net use * http://webdavserver/webdavfolder

It's that easy. Hosting a WebDAV folder is also really easy on any machine with IIS. Just add a virtual directory, point it to your chosen local path and make sure it is browsable.

Bound and gagged

I haven't used much of the built in data binding in .NET yet, preferring to keep things under my own control. However, the current project is using it heavily and I really should get up to speed before it's too late. Here is a small tip that was easy to miss on some of the project's earlier code:

"Try using myBindingSource.ResetCurrentItem instead of myBindingSource.ResetBindings when you need to propagate small data changes throughout an application. It can be much more efficient when you are dealing with big lists."

Scan ahead

In my current project we expect to need to integrate scanning documents into our database. I thought I should do a quick preliminary search on how to control a scanner from a .NET WinForms application. Here are a few links other may find useful:

 Saturday, March 18, 2006

Luser

Windows Vista is expected at the end of the year and security enhancements are one of the biggest talking points about Vista at the moment. One enhancement receiving a lot of attention in particular is the idea of running the system under a Limited User Account (LUA) by default.

I've always like the idea of limited permissions for improved security but it has always seemed too hard to achieve when I have tried in the past. Since then, the information, tools, and push to develop LUA-safe software have become much more popular and it is definitely where most systems are heading. Considering this, I have decided to jump onboard and attempt to use my primary Windows XP machine for all my daily tasks with a non-Administrator, non-Power User, Limited User Account.

I have read several forums and blogs discussing many different aspects of using LUAs but ultimately the replies and comments turn into a list of reasons why people don't like running without their beloved admin rights. I would like to try to use this blog to present a consistently optimistic view toward running with a LUA and look at the problems encountered and how to work around them. Maybe this will be useful to others who are interested in putting their Administrator hat aside for a while or maybe for good.

Writing this article I have been running my notebook as a limited user for one week now. I use my notebook from 9 to 5, Monday to Friday at the office and I take my notebook home at night and on weekends and use it almost every waking hour. It is the only computer I use. At the office I am .NET developer, using Visual Studio 2005 and SQL Server 2005 to write enterprise Windows Forms applications. At home I like to play with the latest software and mess around with new hardware. I definitely consider myself a power user and I didn't begin this expecting it to be an easy endeavour.

However, I may have an unfair advantage. I have had my notebook running with an account with admin rights since I bought it six months ago and in that time I have installed and configured all my regular programs the way I like them. I have now simply removed my account from the Administrators group and continued with my existing profile.

Logging in for the first time as a limited user, my first experiences were very good. All my documents and dev projects were still fully accessible and my regular applications (MSN Messenger, Outlook, and Firefox) worked perfectly. To my surprise Visual Studio 2005 and the SQL Server Management Studio worked fine too.

I am very satisfied with the LUA support in most Microsoft programs so far and I expect most problems will usually be due to bad third-party software. However, Microsoft Backup is an interesting one, as a limited user I don't have access to all my system files so I cannot perform a complete backup. Windows defines a security group called Backup Operators to solve this problem but I am not sure whether adding my normal account to this group would be a good idea. I guess it depends on whether this group only grants access to special file backup APIs or if it grants all access to any file operation. I think I will just login as a special backup user for performing my backups and avoid any potential problems.

Using a notebook PC means that I have a plethora of OEM applications running to "support" all the non-standard features. The power management software is the first of the bad third-party software to fail under an LUA. It simply doesn't run and as a result when my I unplug my notebook and it switches to battery power, the software isn't there to auto-adjust the LCD brightness and other power-saving options. I don't use battery power often so for now I run the power management software as Administrator whenever I need it but I will have to look into this further and maybe start hassling Acer to fix it.

My RSS aggregator, Omea Reader, refused to start, citing an error about class registration or some such thing. A quick search on the newsgroups suggested clicking the Omea "Clip and Edit" toolbar button in Internet Explorer and sure enough the software has worked fine ever since. Also, Nero Burning Rom denied access to my DVD writer, but having encountered this before I logged in as Administrator, ran the NeroBurnRights.exe tool (hiding in the Nero folder), added my account to the Nero group, and that problem was solved too.

Another minor annoyance was the inability to use one of my favourite shortcuts - double-clicking the clock in the system tray to see a quick calendar. As a limited user I cannot change the system time so that dialog is unavailable and I now resort to using my Outlook calendar (which is normally running anyway).

A very handy tool that I must recommend for LUAs is MakeMeAdmin from Aaron Margosis. This tool uses some neat batch file tricks to temporarily promote your current account to an Administrator for certain programs. The benefit of this over RunAs is that with MakeMeAdmin you still maintain your profile, user folders, and environment. I use this tool for installing new programs and for occasionally starting or stopping a service.

So far I am very happy working as a limited user (Luser) and I expect I won't be changing back to Administrator again. I am even becoming more organised with new files that I create or download because I don't have permission to dump them all in the root of my second partition anymore. I will try to keep you up to date with my progress as my LUA experience continues.