Friday, August 3, 2007

Be careful what you name your .exe ...

Windows Vista has some annoying heuristics that look at executables to decide whether they need administrator privileges or not. Unfortunately, these heuristics are fairly brain-damaged.

It seems that putting the word "install" or "update" anywhere in the file name or version properties will trigger the "require administrator elevation" feature in Vista. I had a program that had the word "Updater" in it, which of course frustrated me for a long time as to why Vista wouldn't let me run it as a normal user. I changed "Updater" to "Launcher" and it fixed everything right up.

Oh, and as a side note, don't use the Process class on Windows Vista or Windows Server 2003. Process uses the perf counter to do a lot of its work, which means a normal user can't use it.

Friday, July 20, 2007

Finding the interfaces you want

This is a simple little .NET reflection trick, but one which not a lot of people realize how to do (I get asked about this a lot). Suppose you want to search an assembly for all the types that implement a given interface. For example, your application supports plug-ins and you want to load a third-party assembly that potentially contains plug-in classes that implement your plug-in interface.

C#:

Assembly assembly = Assembly.LoadFile(@"C:\path\to\3rdparty\plug-in.dll");
foreach (Type type in assembly.GetTypes())
{
if (typeof(IMyPlugin).IsAssignableFrom(type))
{
IMyPlugin plugin = (IMyPlugin)assembly.CreateInstance(type.FullName);
// do something with the plugin ...
}
}


VB.NET:

Dim assembly As Assembly = Assembly.LoadFile("C:\path\to\3rdparty\plug-in.dll");
Dim type As Type
For Each type In assembly.GetTypes()
If GetType(IMyPlugin).IsAssignableFrom(type)
Dim plugin As IMyPlugin = CType(assembly.CreateInstance(type.FullName), IMyPlugin)
' do something with the plugin ...
End If
Next


It's pretty straightforward. First, we use the Assembly object to load the third party assembly from its DLL. Then, we use the Assembly.GetTypes() method to iterate through all of the classes present in the assembly. Inside the loop, we use Type.IsAssignableFrom() to determine whether the class can be assigned to our IMyPlugin interface. Finally, we create an instance of the class and assign it to a variable of type IMyPlugin so that we can actually do something with it (note that the above code assumes the class has a parameterless constructor).

Note that you want to check the possibilty of assignment in the order shown above, i.e. typeof(interface).IsAssignableFrom(class). If you do it the other way around, it won't work. Basically this statement is testing to see if this is possible:

C#:

interface variableName = new class();


VB.NET:

Dim variableName As interface = New class()

Friday, June 29, 2007

ADO.NET 101

I'll file this under the "stupid mistakes" category, but I'll admit to it here just in case someone else didn't realize this until it was too late like I did.

When you have a SqlDataReader open, you can't reuse the underlying SqlConnection. In my case, I just changed to using a DataSet instead of a reader (I don't remember why I used a reader in the first place... the query always returned 1 row). Another solution would be to open a second connection.

This support document puts it quite succinctly:

While the SqlDataReader object is in use, the associated SqlConnection object serves the SqlDataReader, and you cannot perform any other operations on the SqlConnection object other than to close it. This is true until you call the Close method of the SqlDataReader object.

Friday, June 22, 2007

Annoying Xcode problem

Taking a bit of a jaunt off into left field from my usual .NET posts...

I was trying to create a Java project under Xcode in Mac OS X 10.4. Strangely, right out of the box the template application project compiled but didn't run. I kept getting a java.lang.UnsupportedClassVersionError.

Seems that after Java 1.5 came to the OS X platform, it gets selected as the default JDK for compilation, but Xcode thinks it's still working with a 1.4 target so it creates a script that starts the app using 1.4.

Once you realize that, it's easy enough to change the settings in the project to fix it. This Apple Q&A explains it in detail.

Thursday, June 21, 2007

References and classes... make sure you mean it

All too often, I see things like this:

C#:
public void DoSomethingToAClass(ref AClass objectToChange)
{
// ... change some member variables of objectToChange or whatever
}


VB.NET:
Public Sub DoSomethingToAClass(ByRef objectToChange As AClass)
' ... change some member variables of objectToChange or whatever
End Sub


This is totally redundant. Classes are reference types. Therefore, by default they are passed by reference. This means that you can drop the ref or ByRef and achieve the same results. What's worse is that in the examples above, you're creating a reference to a reference, which just creates extra work for the application dereferencing them twice to find the actual object.

On the other hand, if what you're passing in to the method is a value type, then this is what you want. Your intrinsic types (such as Int32) and struct (C#) or Structure (VB.NET) are all value types. These need the ref or ByRef tag, otherwise your changes will be lost when the method exits. Note that while Arrays and Strings are also technically reference types, the framework handles them somewhat differently, and you must use ref or ByRef with them to get the desired result.

There are times when a reference to a reference is what you want, especially with certain P/Invoke methods that ask for a "pointer to a pointer" as one of the parameters. In this case, using ref or ByRef with a reference type makes sense and is what you want.