I tried a dozen different things to get this to work, wasting most of a day, and finally asked an expert for help. Before looking at the answer, you might want to see if you can spot my mistake.
The expert told me how to write the
DLLImport correctly (well, almost correctly) off the top of his head, and then asked, by the way, was there any special reason why I wasn't using the
System.Environment.Username property. Well, duh! I'd completely overlooked the
System.Environment class in the .NET Framework, which provides all the information I happened to want for that part of my program.
Even though I wound up using the
System.Environment class in my program, it's useful to look at the full, correct
P/Invoke call to
GetUserName:
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace TestGetUserName
{
class Class1
{
[DllImport("advapi32.dll")]
public static extern bool GetUserName
(StringBuilder lpBuffer,
ref int nSize);
[STAThread]
static void Main(string[] args)
{
StringBuilder b=new StringBuilder(100);
int n=b.Capacity;
bool rc=GetUserName(b, ref n);
Console.WriteLine(b);
}
}
}
What's changed? Two things: one that matters, and one that doesn't matter quite so much. The one that matters is that the second argument to
GetUserName needs to be a reference in C#, since the underlying API calls for an
LPDWORD, in other words for a pointer to a
DWORD.
What was the other change? The best C# type to use for the
LPTSTR lpBuffer argument of the underlying API call is a
StringBuilder, not a
Byte[]. The
StringBuilder returned is directly usable as a string; the
Byte[] would have to be explicitly converted.
If I'd had more experience with
DllImport, I might have realized that the
System.NullReferenceException I got originally was telling me that it needed a reference type where I was passing a value type. Of course, the bug wasn't where I was looking, because I was looking at the wrong place.
There's a rule or three you can take away from this example. The first rule is that a Win32 pointer type translates to a corresponding .NET reference type. The second rule is that a Win32 string pointer generally translates to a .NET
StringBuilder type (or, sometimes, to a
String type). The third rule is to look for a pointer/value mismatch if you get a
System.NullReferenceException from a
P/Invoke call.