Delegates and the AddressOf Operator

Explaining Delegates and the AddressOf operator is a bit tough... So, let’s start at the beginning.

What are they used for?

Delegates are used primarily with API functions as a way to "customize" an API to do something that the author of the API couldn't possibly do in advance. Let me explain using a simple analogy... Let's say that I wrote a program (an API-style DLL) that waited for an alarm to go off and then did something. Since I'm writing this program for others to use, I can't possibility know what you might want to do when the alarm goes off. So, I write my program to use a delegate. That way others who use my program can just "plug in" anything they want into my program. For example, if you wanted to ring a bell when the alarm goes off, you could write a delegate called "RingBell" and plug it into my program. This technique makes it easy for an API programmer, since he doesn't have to predict in advance the actual use of the API by others.

Note: There are other uses for delegates that have nothing to do with APIs, but the concepts are the same as this example.

OK, but why would I ever use 'em?

You probably would never deliberately write a program that required the use of a delegate... in fact, I'd say that the only time you'd ever use a delegate is when you're using somebody else's code (an API-style DLL).

How do I make it work?

Well, there are four steps to the process.

How about a real life example

Here is an example that returns the "Windows Text" (typically the title bar) of every running application. It also includes the text of all of the buttons and controls on each of those windows.

Step 1: The Delegate keyword is used to create a "signature" that will later be used like as a "type" in the declaration of an API function. Take a look at the following:

Private Delegate Function EnumCallBackDelegate( _
   ByVal hwnd As IntPtr, _
   ByVal lParam As Integer _
) As Boolean

...this line of code doesn't do anything... it's just creates a "signature" (that's made up of the number and type of parameters, plus their order).

Step 2: OK, now it's time to use that signature... it's time to declare the EnumWindows API function.

Private Declare Function EnumWindows Lib "user32" ( _
   ByVal lpEnumFunc As EnumCallBackDelegate, _
   ByVal lParam As Integer _
) As Boolean

...notice the 1st parameter of the API is "ByVal lpEnumFunc As EnumCallBackDelegate"? See how the delegate is used as a "type" (like "As Integer")

OK, we're done with the Delegate for rest of the program! Yep, after you've used it inside the API declaration, you'll never need it again.

Step 3: So, now it's time to create a function that you intend to use with the EnumWindowsAPI function.

Recall that this function *must* match the signature that you got from the API documentation (and used in the Delegate line). Obviously, the name of the function doesn't matter... you can call it anything you want. But in this example, the first parameter *must* be an IntPtr, and the second parameter *must* be an Integer. So, consider the following:

Private Function EnumCallBack(ByVal hwnd As IntPtr, ByVal lParam As Integer) As Boolean
   Dim TextSize, ret As Integer
   Dim WinText As String

   ' get the size of the text in the window
   TextSize = GetWindowTextLength(hwnd)
   If TextSize > 0 Then
      ' create a string big enough
      WinText = Space(TextSize + 1)

      ' get the windows text
      ret = GetWindowText(hwnd, WinText, TextSize + 1)

      If ret > 0 Then
         ' store this Window Text
         sb.AppendLine(WinText.Substring(0, ret))
      End If
   End If

   ' The API documentation says to return True to continue with the
   ' enumeration of windows
   Return True
End Function 

... this is the part where you "fill in the blanks".... to customize the API to do whatever-it-is you want it to do. In this example, it records the Windows Text

Step 4: The last step is where you plug your function into the API using the AddressOf keyword. In this example, the GetWinText function uses the EnumWIndows API to collect all of the windows text and it then returns the text as a single string. Take a look at the following:

Public Function GetWinText() As String
   sb = New StringBuilder

   EnumWindows(AddressOf EnumCallBack, 0)

   Return sb.ToString
End Function

...notice the 1st parameter to the EnumWindows API is "AddressOf EnumCallback". EnumCallback is the name of the function we created in Step 3 (not the name of the Delegate). The compiler will check to see that the signature of the EnumCallback function exactly matches the signature of the delegate used when we declared the API function.

Download/Links

Download the VB.Net 2005 Source code example used in this article: DelegateDemo.zip
Download a related VB.Net 2005 Source code example: FileSystemWatcherDemo.zip