Tuesday, November 29, 2005

How to pre-populate assemblies to the Add References dialog in Visual Studio 2005

This one is good to keep track of when working in a team enviroment where everyone uses their own folder structures.

Here are some options I found that worked for me when I tried them on my machine:

  1. Add a new sub-key under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx (or the same sub-key under HKEY_CURRENT_USER if you want the assemblies to appear only for the current user instead of all users).  The default value of the sub-key should be the folder path that you want Visual Studio to look in for assemblies to include in the Add References dialog.  This registry path is specific to VS 2005 and the .NET Framework 2.0 and will not work for previous versions of VS or the .NET Framework.  It is documented in this MSDN document.

  2. Add a new sub-key under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders (or the same sub-key under HKEY_CURRENT_USER if you want the assemblies to appear only for the current user instead of all users).  The default value of the sub-key should be the folder path that you want Visual Studio to look in for assemblies to include in the Add References dialog.  This registry path is global and any folders listed here will be processed by VS .NET 2002, VS .NET 2003 and VS 2005 as well as the .NET Framework 1.0, 1.1 and 2.0.  It is documented in this KB article.

  3. Place a copy of the file in the folder c:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies.  This path is dependent on the version of Visual Studio that you have installed and whether or not you installed it to the default path, so you may need to adjust it as needed for your system.  The path that I list is for a default install of VS 2005.


[Aaron Stebner's WebLog]

Monday, November 28, 2005

Installing SQL Server Express

There's a good article on MSDN for controlling the installation of SQL Server Express. If you are bundling Express with your application, you may want to handle the Express options to make it simpler for the user. There some nice code for determining if Express is already installed.


I was reading some of the older postings in James Bach's excellent blog, Training the minds of testers, when I found a little gem called "counterstrings".

A counterstring is a graduated string of arbitrary length. No matter where you are in the string, you always know the character position. This comes in handy when you are pasting huge strings into fields and they get truncated at a certain point. You want to know how many characters that is.

Here is a 35 character counterstring:


Each asterisk in the string occurs at a position specified by the immediately preceding number. Thus, the asterisk following the 29 is the 29th character in that string. So, you can chop the end of the string anywhere, and you know exactly where it was cut. Without having to count, you know that the string "2*4*6*8*11*14*17*2" has exactly 18 characters in it. This saves some effort when you're dealing with a half million characters. I pasted a 4000 character counterstring into the address field of Explorer and it was truncated at "2045*20", meaning that 2047 characters were pasted.

I realize this is may not be a very interesting sort of testing, except perhaps for security purposes or when you're first getting to know the app. But security is an increasingly important issue in our field, and sometimes when no one tells you the limits and dynamics of text fields, this can come in handy.

[James Bach's Blog (Testing)]

This is a really cool and simple way to verify the length of an entry field in an application. He also has a small applet, downloadable from that blog entry, that will let you create counterstrings of various patterns. I may code a clone up in Delphi for my own nefarious purposes.

Friday, November 25, 2005

Fun with the MSDE: installing

Dana Epp has posted a request for information about the MSDE on his blog. He wants to install an app that that requires a named instance of the MSDE and wants to include the smallest footprint of the MSDE installer bits. On machines that already have the MSDE, he wants to install an additional named instance and wanted to avoid including the MSDE bits since it was already installed. With SQL Server named instances, you can't do that. Adding a second instance isn't a reconfiguration of the existing install of SQL Server/MSDE/Express, it's a completely separate install. It's considered to be a separate application by the installer and the OS.

I'm not sure why Microsoft went that way, but it's tied to how the Windows Installer works. A Windows Installer file (.msi) installs just one install of itself. If you try running it again, the installer locates itself on the machine and brings the installer up in maintenance mode. It's not designed to let you install multiple copies of a single application. If you want to do that, you have to go down the multiple instance transform route. A multiple instance transform file (.mst) is basicly the difference between two .msi files. You use the .mst file to change some of the values in a .msi file so that it gets considered to be a different installer by the Windows Installer runtime. By that's a whole 'nother story in and of itself.

Because each instance is essentially a separate installation, you don't want to mess around with the source installer bits. Otherwise, you'll make it difficult to install patches or upgrades. I'm pretty sure that's why Microsoft prefers that you to use their executable to install SQL Server/MSDE/Express. If you use the .msi files, then the patches have be generated against the .msi files that you used. If you merged the SQL Server .msi into your .msi, then upgrades to SQL Server on that machine will required your .msi file. Ouch.

Each instance has to have a .mst file, it's a one to one relationship. I'm not sure what MS is doing under the hood in the SQL Server install process, but my guess is that it's creating .mst files at runtime when you specify an instance name. Otherwise it have to include X number of .mst files in the package, and if they did that some twisted person would try to install X+1 instances and break the installer. Creating a .mst file on the fly is doable, but it's not something I would want to implement. When the user specifies a new instance you need to take your .msi installer file, copy it to a new .msi file. Then you modify that .msi file with the instance name, increment a property, and generate a new product GUID. You then generate the .mst file from the difference between the original .msi file and the new one. After that, you delete the new .msi file, it's no longer needed. Then you call msiexec with the .msi file and the .mst with the appropriate parameters. I'm not sure that you could reliably generate installer patches (.msp files) under that scenario.

I'm not a fan of how Microsoft implemented the multiple instances. When I wrote the server end of e-Link, I used the Windows Installer to install the service. If the user wants to install a second instance of e-Link, they can use an administration utility that I wrote that can register additional instances of the service, but using the same executable. It provides the same functionality to the user, but only requires one set of files. This makes updating the service much easier as the patch only has to target one installed instance.

I can see why MS went along the single instance route with Windows Installer. It prevents the user from accidently installing 8 copies of "Hunt the Wumpus". It makes it easy to allow installers to repair existing installations or modify the original setup. It does makes some tasks extremely difficult for installing. The big thing that I can see what would require multiple installs are web sites. There you could have multiple installations as people usually install a web a couple of times to try different settings, of have "live" and "staging" versions of a site.

If you want to install a web site with Windows Installer multiple times on the same machine, you have a few choices.
  • You could limit the install to a single instance. If the user wants multiple copies of the web site files, they have to do that part manually. We do that with the web gui end of e-Link and we document how to do it. Another way (which I have not tried) would be to rename the web site or virtual directory and it the physical name of the folder that it was installed into. Then run the installer again. It will come up in maintenance mode and you would select repair and it should install another set of files into the previously selected location. The draw to both methods is that the installer will repair or uninstall only the files it knows about. If you add anything manually, then you must remove it manually.

  • You could supply a fixed number of instance transforms. If you do this, you'll need an intelligent front end application to your installer so that you can prompt the user for the instance name. Then you have to check to see if that instance is already installed. If not, then you get the next available instance number and see if you have enough transfoms to use one for this install. Lots of error checking required here, it's not something I would want to do.

  • You could generate instance transforms at run time. Much of the same logic as the previous option, plus the additional work of creating the transform. I definitely don't want to ever have to do this.

This is why you don't see many .msi files for installing web sites.
Trackback to Dana's post

Wednesday, November 23, 2005

RE:How to determine if the current user is an administrator

This is pretty cool. A lot of the stuff that I write is low level admin type of utilities. As my coding meter moves further away from the Win32 Delphi to C#, I'll need stuff like this more often.

Following, is the method that I use to determine if a user is an Administrator. Note that in .NET 1.0/1.1 the using construct wrapped around the creation of the WindowsIdentity won't compile as its implementation of IDisposable is new for .NET 2.0. Previously you had to rely on the finalizer of the object to run so that the handle of the user token could be freed.

        using System;
        using System.Security.Principal;
        using System.Windows.Forms;

        public static bool IsAdministrator
                if (SystemInformation.Secure)
                    using (WindowsIdentity identity = WindowsIdentity.GetCurrent())
                        WindowsPrincipal wp = new WindowsPrincipal(identity);

                        return wp.IsInRole(WindowsBuiltInRole.Administrator);

                return false;

There is also a check to determine if the operating system is secure (ie Windows NT platform) - the documentation states that the IsInRole method returns no results on Windows 98 and Windows Millennium Edition. I'm not entirely sure how a method that returns a boolean value can return no results, so it is wrapped in the Secure check just in case an exception is thrown when using it on Windows 9x/ME.

from [Managed from down under]

Monday, November 21, 2005

Why you wont find C# example code for writing Shell Extension handlers

Jesse Kaplan posted the following (thanks to Junfeng Zhang)

Writing in-process \shell extensions in managed code is actually a very dangerous thing to do because it has the effect of injecting your managed code (and the .NET Framework) into every application on the machine that has a file open dialog.

The problems occur because only one version of the .NET Framework can be loaded in a process at any given time (other shared components such as java and msxml have the same property and thus the same restriction).

If you write your shell extension using the 2.0 .NET Framework and an application built with the 1.1 .NET Framework uses a file open dialog, your shell extension will fail because it can not run on an earlier version. Things can get even worse if your shell-extension manages to get loaded in a process before another applications managed code does: your extension may force an existing application onto a different runtime version than the one it was expecting and cause it to fail.

Because of these problems we strongly recomend against using any single-instance-per-process runtime or library (such as the .NET Framework, java, or msxml) in an in-process shell extension.

Interesting scientific study about font usage on web pages.

Jeff Atwood posted a cool article from Wichita State about font legibility. Serif mono-spaced fonts were the worst (sorry Courier) and Tahoma did the best.

The study is worth reading, the testing methodology was objective and at pretty much validates what fonts you think should be used for a given type of web site. The other good tip in Jeff's post was that Software Usability Research Laboratory (SURL) at Wichita State has an excellent newsletter that documents their usability studies.

Client vs Developer

Client vs Developer wars.
This is stuff isn't directly applicable to our development patterns, but it has some useful information.
"Grayscreen" prototyping (building a simple HTML model of a website) provides an extremely effective way of communicating a website's content, structure, and functionality before design and programming begin. Prototyping overcomes many other difficulties commonly associated with web development, such as managing expectations, communicating effectively, working through the design process, and creating and delivering content.
It's another example of how design up front methods save time and money.

Good article plus a free book in PDF format.

Thursday, November 17, 2005

Grimmy weighs in on blogs

It's subtle, but I like it. If I was ever to write a blog posting tool, the icon for posting the entry would look like the first panel in that strip. If you like that strip, you can access at the archives at Grimmy.com.

Wednesday, November 16, 2005

That pesky SQL Server Express. I'm going to have to support it, it's time to start collecting tips from the veterans....

These problems left me stranded for days, then today, on another machine, I hit a different set of problems I kept getting the following "Failed to generate user instance of SQL Server due to a failure in starting the process for the user instance. The connection will be closed." and if I set "User Instance" to false the following "An attempt to attach an auto-named database for file C:\[app_path]\ASPNetDB.mdf failed. A database with the same name exists, or specified file cannot be opened, or it is located on UNC share."


If you don't want to follow the link, the short answer is that SQLExpress creates a directory per user in "c:\Documents and Settings\[user]\Local Settings\Application Data\Microsoft\Microsoft SQL Server Data\SQLEXPRESS" that it uses to store information. Deleting this directory has fixed both of my problems.

[from Enjoy Every Sandwich]

New tool bar icons for VS 2005

I'm putting this here to I'll remember where the bits are

A customer from Las Vegas DevConnections asks:

It seems like, but I may be mistaken, that you said something about posting some new set of toolbar icons on your blog? Was I in a Vegas-induced stupor or did you really say that?

Yes, I really, really did - however I totally forgot to link to it.  Aaron talks about the library here.  ...and Soma talks about how the new image library came to be

Instructions for finding the image library can be found here:

To install the Visual Studio 2005 Image Library
Locate the file VS2005ImageLibrary.zip. This file is normally installed in \...\Program Files\Microsoft Visual Studio 8\Common7\VS2005ImageLibrary\.

  1. Right-click VS2005ImageLibrary.zip and click Extract All.

  2. The Extraction Wizard appears.

  3. Follow the directions in the wizard to extract the images.

It looks like, this is something you'll need the full Visual Studio install for - the zip file does not install with the Express editions of Visual Studio.


[via jfo's coding]

Tuesday, November 15, 2005

File this one under "Huh?"

Rick Moranis has a Country Album. I'm having trouble picturing The Keymaster singing honky tonk, but that's just me.

Building ASP.NET 2 projects

On K. Scott Allen's blog, you can find a really good description of how the build process for ASP.NET projects has changed from 1.0/1.1 to 2.0.

One of the adjustments to make when moving from ASP.NET 1.1 to 2.0 is how to produce debug and release builds.


Here is the most important concept to come to terms with in 2.0: Visual Studio 2005 knows nothing about compiling a web application. In 1.1 VS built the code-behind and ASP.NET built the web forms. In 2.0 Visual Studio 2005 delegates all compilation responsibilities to the ASP.NET platform.


Jump here for the full article, it neatly describes what has changed and how it works. He doesn't go into why it changed, that's the part I don't get. What's the advantage of the new build method? In the end, as long as I can get FinalBuilder to build it, I'll be happy.

Monday, November 14, 2005

Test drive of writely.com

This message was entered with writely.com, a web based document collaboration tool. I usually enter my blog entries with w.bloggar, but I'm game to try something new. It doen't work with Opera 8.5, I'm using IE 6 right now. As a rule, I only use IE when I have to do, and that's enough to limit my usage of writely. It does work, it's just a little bit sluggish.

A suggestion for "Just Think of Canada as 11 More States"

Steve's has a bone to pick with us.

I’m a loyal Canadian. I enjoy Tim Hortons, and don’t mind the Canadian Tire guy. But as Canadians, I think we need to get the message across to Americans that we’re not all that much different from them, and not that far away.

Why do I say this? Because one too many times I’ve been shopping online, found the exact product I want, and then find the dreaded statement:

Sorry, we are able to ship (and mail printed catalogs) only to the US, its territories and possessions, and APO or FPO addresses.

Why? Why why why?

What you need to do is convince FedEx and/or UPS to offer virtual mail addresses. Packages sent to a virtual address would automaticly get forwarded to a real Canadian address, with the recipient paying the additional fees. It's mailbox forwarding, except with non virtual bits. It's a win-win situation for everyone. Steve gets his reasonably priced borosilicate lab glassware, the vendor makes a new sale, and the delivery company makes a few dollars (or loonies).
Allen Bauer has a cool post on reporting bugs and the common sense etiquette that should be used.

I hope folks understand that by following a few simple rules and creating a defect report that you would like to get is actually you contributing to your own success.  In this one case I have to say that karma is the best way to describe this process.  But paying it forward and actually becoming a part of the solution, it will all eventually come back to you.  Even though you may think your contribution is tiny and insignificant, the fact of the matter is that your contribution when compounded with everyone else's can pay back in large dividends.

Having been on both ends of the defect report, I completely concur with Allen. A long time ago (and in a galaxy far away), I was moonlighted as the tech support "department" for a well known reporting package that used to be installed with Delphi. On average, I would get between 30 to 60 emails a day from other developers who bought this package. It never failed to amaze me how hard it would be to get people to send specific information to duplicate a badly worded or vague bug description. Mind you, this reporting package had plenty of issues to deal with, but many times I couldn't duplicate the bug that was reported by the description that was included. I would usually send a followup email asking for the steps required to duplicate the bug.

I tried to avoid getting sent entire projects. This was 1997-2000, and I was doing support from a dialup account, it woul take forever to download everyone's projects as they usually sent me everything, binary files included. Sometimes, I needed the full project to duplicate the problem. That part was always interesting, you learn that the users will use the code in ways that you did not expect. There were always a few people who would refuse to provide detailed information and I would get a ranting email about not wanting to peform QA work for free. I actually received an invoice from one guy for the "services" that he had provided in tracking down the problem. [more about that when I finally decide to blog about that job].

On the other end of the spectrum, I spent a considerable amount of time documenting flaws in Sybase's ADO provider for Adaptive Server Anywhere. In the beginning, they wouldn't take my bug reports seriously. They would tell me that it was a Delphi ADO issue and not a problem with their provider. The problems were not with Delphi or it's ADO components, but they couldn't duplicate the bugs from my descriptions and they were not inclined to look at Delphi source code. I ended up coding detailed examples as VBScript files. One of the nice things about ADO was that it behaved the same way no matter who it called it. With those examples, I was able to get Sybase to spend time tracking down the problems that I was having. They were never able to fix those problems, but I did appreciate their efforts. What came out of those examples was that I was using Sybase's ADO provider in a different way than they were testing and apparently, their testing did not include all of the features supported by the provider.

Friday, November 11, 2005

RE: A better BackgroundWorker : CancelImmediately and other goodies

Hmm, I can use this for a service testing application that I'm working on...

One of the nice new features you get in .NET 2.0 Winforms is the new BackgroundWorker component. It allows safely executing long tasks in a different thread than the GUI, while allowing an easy event-driven interface to perform the task and respond to events (such as detecting work progress). There are even versions of this for .NET 1.1 courtesy of Juval Lowy.
One of the things it does not support is the ability to Cancel the running task immediately rather than waiting for the code to process the Cancel Request (which is vital if your "DoWork" code contains tasks that individually take a long time to process). With some Reflectoring, I was able to overcome this obstacle and provide a solution for this problem.
For the full details and download in VB.NET and C#, read the full article:
[ISerializable - Roy Osherove's Blog]

[Update] There's a good article by Juval Löwy, titled Asynchronous Windows Forms Programming that covers this in great detail

Another way of deleting data in chunks

I'm bloggin this because I'll need it in a few weeks....

Michael Campbell has blogged about removing data from a table in chunks http://sqladvice.com/blogs/repeatableread/archive/2005/09/20/12795.aspx. This can be simplified even further in SQL Server 2005

DELETE TOP (2000) FROM MyBigTableWHERE someCondition = true  ORDER BY Dateolumn ASC


  [ via WebLogs @ SqlJunkies.com]

Thursday, November 10, 2005

Who was Microsoft kidding when they promised to end "DLL Hell"? The long dark night of the SQL Server 2005/Visual Studio .NET 2005 beta produced a series of seeming incompatible .NET frameworks. I didn't do much beta testing with the 2005 bits, but when I did, I did so from the safe confines of various VMWare sessions. I'm now looking at some automated build tools and one of our developers suggested MSBuild. That's the last thing you would want, a build tool that will be dependant having the appropriate runtime installed. Plus I want something that supports more than .NET compilers, we are still building Delphi Win32 apps here as well as .NET assemblies.

On a slightly separate target, why RSS Bandit is such a resource hog? I loved the interface, but it would bring the performace of my PC to a flying stop. I just started using Omea Reader, and it plays well with others much better than RSS Bandit did.

....Microsoft promosed that DLL Hell would go away, but with the .NET Framework betas, they dropped us into the biggest DLL Hell I’ve ever seen. I can’t uninstall MSH from my system because I installed the .NET Framework 2.0, which required that I uninstall the beta versions. The MSH uninstaller requires the beta framework, which I uninstalled, so I’m stuck. I can’t install the release version of MSH. This sort of thing is just silly, and I thought it was a problem that we’d solved. (To be fair, they did promise that this won’t be a problem for release versions of the framework, but how many millions of times were the betas downloaded?)

from Stevex's blog

Wednesday, November 09, 2005

See What Services Are Associated with a Process?

There's a cool tip on the tech-recipes site to show how to see what services are being run under each instance of svchost.exe. From a command line run the following:

tasklist /svc /fi "imagename eq svchost.exe"

That will list each instance of svchost.exe with all of the services being run by that instance.

Bender, Smender. I want Robbie!

Forbidden Planet has always been one of my favorite movies. As a kid, I always wanted my very own "Robbie the Robot". If I start playing the lottery, I can now order one from the Hammacher Schlemmer catalog for the mere sum of $49,999.95.

Robbie was a cool robot. None of this wimpy C-3PO nonsense. Robbie embodied the cutting edge of 1950's computer technology. Vacuum tubes glowing, relays clacking, and don't forget little metal spinning things on the side of his head. That's a man's robot.

I know a few people who were deeply affected by that movie. There used to be an audio store just outside of Albany, NY, named "Altair Audio". It was named after the planet Altair IV. It was actually a decent place, they carried the high end stuff and knew how to sell it. And of course, they carried the Krell line of amplifiers.

And the movie had Leslie Neilsen back when he was known as a dramatic actor. It's hard to watch him in serious roles after seeing Airplane and the Naked Gun shows/movies. I actually Leslie Neilsen once. About 15 years agao, I went to Irvine CA to visit my brother. On the way back, I flew out of John Wayne (aka Santa Ana) airport. While I was waiting for my fliight, I saw a production crew fiming a commercial. The commercial featured Leslie Neilsen. He had a break in the filming and I walked up to him and introduced my self. He was very gracious and we chatted for a minute or two. I was probably stammering like an idiot. I was too nervous to get a picture of the two of us. I do have a blurry picture of the back of his head.

Then a couple of older women came up. They were huge fans of his dramatic roles and they just kept talking and talking. They would ask him a question and then talk about something else before he could answer. And that's when the fun began. Mr. Neilsen had a llittle device in his suit jacket pocket that made fart sounds. He would stand there and smile at the women or make small talk, and at the same time, he was pressing the button on the fart sound device. The women didn't know how to react. They were trying to get their cues from Leslie, but he played it straight. I wanted to laugh so hard, I was dying. If he was goiing to play it straight, I was going to do so. I wanted to see how far he could take this. After a few minutes, I had to leave to catch my flight. I never knew how far he was able to play the gag, but I still laugh to myself just picturing it in my head.

Monday, November 07, 2005

More news on Sony's rootkit

The story of Sony's rootkit continues to live on. After Mark Russinovich first documented his analysis of the rootkit installed by content protected Sony audio CD's, just about everyone and their grandmother linked to Mark's site. Oddly enough, CNN didn't. That's another story. Mark has provided updates to the rootkit story and it continues here.

The latest addition to the story is still worth reading. The company that wrote the rootkit, First 4 Internet, responded to Mark in a comment on his blog. They address his concerns item by item, and of course, obsfuscate the issues. Mark's latest blog entry goes over First 4 Internet's comments, blowing huge gaping holes on each of their points. When this finally blows over, the Sony/First 4 Internet saga will be a text example of how NOT to address a growing PR disaster.

Thursday, November 03, 2005

The arrival of Delphi Man and Doctor Deadline

After coming back to Cubeland from lunch, I saw a small package on my chair. It had airmail stamps and came from Middlesex, England. I had no idea what it was, so I ripped it open to find Boland's inflatable action figures. The images are courtesy of the crapmatic instant picture device. The top one is Delphi Man, lord of the cubicle. The lower picture is of Doctor Deadline, trusty companion of Delphi Man.

There are limited edition items, Borland only made 1000 of each one. So far they haven't turned up on eBay. Oddly enough, the website that advertised the super duo, www.delphisuperhero.com has been replaced with the Delphi 2006 web site. In addtion to registering to win the inflatable characters, there were PDF files that you could download and print your own.

Wednesday, November 02, 2005

China's Little Green Book

Thomas Friedman had an interesting column today. It's titled "China'a Little Green Book" and he talks about how the growing conservation movement in China. China is poluted to a scale beyond anything we ever saw here. When we were over there in the summer of 2003, I was astounded by amount of smog in the air. It's good to see that they are going to doing something about it.

Tuesday, November 01, 2005

Sony now installs a flawed rootkit

This is lovely. Sony is now putting a rootkit installer on their Music CD's. Mark Russinovich discovered a root kit on his machine while testing a rootkit detection tool (RootKitRevealer   ) that is was working on. He documented the process on his blog and it's required reading. He cleary documents what he found, how badly written it was, and what he had to go through to remove. The rootkit installed by Sony is a Digital Right Management service, but is so badly written it will have all sorts of nasty side affects. This thing will really mess up your computer. If you don't remove it correctly, you will lose access to the CD/DVD drive in your PC.

Debugging services

Simon Carter has a tip on debugging services. It boils down to calling the Windows Sleep API in your service constructor code. This gives your debugger enough time to attach the your process in time to step through the service start up code. Usually when you attach to a running process, it has already gone through it's normal initialization code ("We now join this program already in progress"). If you are trying to diagnose a problem in the startup code, it's already too late by the time you can attach to it. That is the joy that we call Windows Service programming.

I prefer to take a different tack for debugging a service. I separate the code of what the service is supposed to do from the actual service code. I'll build a Windows App (or Winforms app) that calls the same code that the service would call. It's much easier to debug the code from an application instead of a service. 99.9% of the time, the problem is not part of the service controller code, it's the in the code that service uses. The other 0.01% of the time where the problem is in the service creation code, I just add a lines to send data to the Windows event log. This technique works well for Win32 Delphi and .NET programming.