Post

Declare vs. DLLImport

Paul Vick asked an interesting question about my last post concerning why I chose to use the DLLImport rather than the Declare statement. I was going to quickly respond stating the old “well, it seems to be the .NET way of doing it” or the “it allows me to specify the SetLastError=True" portion.

So rather than answer the question right away, I took a few moments to actually second guess myself as to why I was doing it one way vs. the way we’ve always done in the past. As I was thinking about this, more questions were raised in my head. Why am I doing it this way? Is there a benefit to one over the other? What limitations exist by doing one vs. the other? I suppose the original reason why I started using it with the DLLImport attribute was so that I could specify the SetLastError to True so I could actually check for a Win32 type error. If you look up the help for the Declare statement, it states nothing about whether when using a Declare statement if the error will be passed in automatically (ie. SetLastError = True). However, if you follow one of the links at the bottom of the page to the LastDllError method, you will find a sample that shows the usage of the Declare statement and it appears that the SetLastError value is indeed set to a default of True when using Declare.

Now, the question is raised. Can you use all of these things together (DllImport/SetLastError, Declare, Marshal.GetLastWin32Error and Err.LastDllError)? The answer apparently is a huge yes. I created two versions of the Interop declaration:

1
2
3
4
<dllimport("kernel32.dll", setlasterror:="True)"> _
Public Shared Function Beep(ByVal frequency As Integer, ByVal duration As Integer) As Integer
End Function
Private Declare Function Beep Lib "kernel32.dll" (ByVal frequency As Integer, ByVal duration As Integer) As Integer

And used the following code to test some of the variations.

1
2
3
4
5
6
If Beep(0, 2) = 0 Then
  MsgBox("Error: " & Err.LastDllError)
End If
If Beep(0, 2) = 0 Then
  MsgBox("Error: " & Marshal.GetLastWin32Error)
End If

All of the previous calls will result in an Win32 error of 87 since we are passing in an invalid value for the frequency. When you set SetLastError to False, you will get unpredictable results for the Beep() version (my tests resulted in 127 and 126; changing the order of the calls made no difference… the first time was 127, the next was 126 ???????).

Now to answer the question by Paul. I suppose it was partially ignorance on my part for not validating what I was assuming. The other part was all the pressure from the C# camp stating to do things the ‘.NET purist’ way. Of course, it’s questions like this that make one think. Yes, the Declare statement is a whole lot easier on the eyes, requires less typing and allows for a lot of whitespace to be removed. Under most circumstances, the Declare statement will do the job very nicely. There are a few instances where the other method needs to be done to allow for more granular control over the declaration. For example, if for some odd reason you need to turn off SetLastError. Not sure why you would need to, but I’m sure theres a need for it somewhere. Also, with .NET 1.1, a couple of new features were added to the DllImport attributes related specifically to how Unicode is handled.

Now that I’ve actually taken the time to look into this, I’m definitely going switch back to the Declare statement. It is a whole lot easier to work with and is much cleaner to read through.

Thanks to Paul for making me re-evaluate my thinking on this.

This post is licensed under CC BY 4.0 by the author.