March 2005 - Posts

VSM arrives in "style"?

The latest issue of Visual Studio Magazine arrived in a special envelope from the post office. A pre-printed message on the envelope apologized for the delay in getting the item to me. It had encountered some "problems" en route to me. I opened the enveloped and discovered a large chunk of the magazine missing:



Yikes!
Posted by PSteele | 5 comment(s)

What's "/u" for?

Got this one from a colleague today:

http://support.microsoft.com/kb/q210565/
Posted by PSteele | 1 comment(s)

VB.NET Shares a little too much

A thread today on using the System.Diagnostics.Process class highlights one of the more confusing aspects of VB.NET (or VS.NET depending on how you want to look at it). Intelliense will list Shared (static in C#) members when showing members on an instance variable. Take the following sample code:

Option Strict On
Option Explicit On 

Imports System
Imports System.Diagnostics

Public Class Class1

    Shared Sub Main()

    End Sub

    Public Sub UseInstance()
        Dim p As Process = New Process

        p.Start("http://www.microsoft.com")
    End Sub

    Public Sub UseShared()
        Process.Start("http://www.microsoft.com")
    End Sub

End Class

In 'UseInstance' the "Start" method appears to be a method on an instance of Process. But then in 'UseShared', we see that "Start" is a shared method on the Process class. Further, if you compile this code and check it out with ILDASM you'll see that both calls compile to the same IL:

ldstr      "http://www.microsoft.com"
call       class [System]System.Diagnostics.Process [System]System.Diagnostics.Process::Start(string)

So be careful when you're using VB.NET. Some methods may not need a class instance to be called. This could be especially annoying on a class that implements IDisposable. You'd create an object that would hang around longer than normal (since it's Disposable) but may never actually use it if the method you're calling is shared! Perhaps VS.NET 2005 could make this configurable...

Posted by PSteele | 2 comment(s)

Exposing Events in User Controls hosted in Internet Explorer

NOTE: This is an old blog post that I obviously never posted. I wanted to send a link to this article to someone and when searching through my archives I realized I never posted it! Enjoy.


Because of architecture restrictions (too difficult to explain in a few sentences), I had a situation where an ASP.NET implementation had to let the client execute a function and return the results for display in ASP.NET. I originally considered a smart-client application with no-touch-deployment since all clients will have the .NET framework, but the application UI had to be in ASP.NET. So I decided to use a WinForms User Control hosted in Internet Explorer.

Getting a sample control up and running is relatively simple. Create the UserControl DLL, copy it to the web directory, add an <OBJECT> tag to the ASPX page and set the CLASSID accordingly (CLASSID="<dllname>#<full-namespace>.<classname>". Now I had the user control doing the processing and I wanted it to raise an event when complete. The client-side javascript code would capture the event, place the data in a hidden form field and submit the form back to the server. Seemed simple enough until I tried to add the event.

No amount of tweaks and hacks would capture the event I raised in the .NET user control. I did some digging and found a couple of newsgroup postings as well as an MSDN article detailing the steps necessary to get a UserControl hosted in IE to raise events properly. It comes down to needing old, COM-style event source sinks. I can't give you a full explanation on this since but you'll find a nice deep article about it on MSDN. However, a little refactoring and a few attributes got me what I needed.

First, you need to define an Interface that contains the names and signature of the events you want to expose. The interface needs two attributes:

1) InterfaceType(ComInterfaceType.InterfaceIsIDispatch) -- define an IDispatch interface
2) Guid(...) -- define a GUID for the interface

In addition, each method in the interface needs a DispId() attribute with a unique value applied to it. Here's a sample interface for an event called "InfoMessage":

using System;
using System.Runtime.InteropServices;

namespace SimpleControl
{
	[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
	[Guid("C89B3E02-E523-422e-8312-D5B8F6F63904")]
	public interface IClientControlEvents
	{
		[DispId(1)]
		void InfoMessage(string message);
	}
}

Now, to hook this into the UserControl, you need to add two attributes to your class:

1) ClassInterface(ClassInterfaceType.None) -- Prevent .NET from generating a default interface for this class.
2) ComSourceInterfaces(typeof(IClientControlEvents)) -- Define the interface for COM event source sinks

All that's left is to use the regular .NET delegate/event mechanism for event handling. Just make sure the event name matches the name defined in the interface:

	public delegate void InfoMessageEventHandler(string data);
	public event InfoMessageEventHandler InfoMessage;

Capturing the event at the client will depend on which scripting technology you're using. For VBScript, simply create a method with the usual "<id>_<eventName>" scheme. For JavaScript, you need the <script> tag with the "for" and "event" attributes. Here's an example:

<object id="myControl" classid="samplecontrol.dll#MyNamespace.ClientControl" />

<script language="vbscript">
	Sub myControl_InfoMessage(data)
		MsgBox(data)
	End Sub
</script>

<script language="javascript" for="myControl" event="InfoMessage(data)">
	alert(data);
</script>

The only other thing to watch out for is that any public methods you want to call through script need to be done via an interface implemented by your UserControl.

SOURCE: http://msdn.microsoft.com/msdnmag/issues/02/01/UserCtrl/default.aspx
Posted by PSteele | 9 comment(s)

Do you "organize" your 'using' statements?

More and more I find myself "grouping" my using statements ("Imports" in VB.NET). For example, I now try and do Microsoft framework namespaces first, then namespaces for DLL's developed at my employer and finally third party namespaces. Something like this:

using System;
using System.IO;
using System.Data;

using <company>
using <company>.<module>

using <third-party>
using <third-party>.<module>

Not sure why I do this. If anything, it gives me a rough idea of the types of references in the project. Those "groups" usually belong to a specific set of assemblies.

Or maybe I'm just not getting enough sleep... :)

Posted by PSteele | 15 comment(s)

Timing is everything

I was doing some code reviews today. The particular project I was reviewing was doing COM-interop so one of the things I looked for was to make sure that any COM objects created were wrapped in a try/catch/finally block and those objects were properly released with Marshal.ReleaseComObject in the finally block.

I was thinking that the code would be cleaner if, inside the finally block, I could access variables created in the try block. Since you can't do that you're forced to define the variable outside the try/catch block.

Scott Wiltamuth just addressed a very similar question in his blog today.
Posted by PSteele | with no comments
More Posts