Everything I know about .Net programming tells me that the behavior I see here is completely impossible. So can someone please explain what is going on here? Simple code:
Structure WKSTA_USER_INFO_1
Dim wkui1_username As Integer
Dim wkui1_logon_domain As Integer
Dim wkui1_logon_server As Integer
Dim wkui1_oth_domains As Integer
End Structure
Declare Function NetWkstaUserGetInfo Lib "Netapi32" (ByVal reserved As Integer, ByVal level As Integer, ByRef lpBuffer As Integer) As Integer
Declare Sub lstrcpyW Lib "kernel32" (ByRef dest As Byte, ByVal src As Integer)
Declare Sub RtlMoveMemory Lib "kernel32" (ByRef dest As WKSTA_USER_INFO_1, ByRef src As Integer, ByVal Size As Integer)
Declare Function NetApiBufferFree Lib "Netapi32" (ByVal Buffer As Integer) As Integer
Function GetDomain() As String
Dim ret As Integer
Dim wk1 As WKSTA_USER_INFO_1
Dim pwk1 As Integer
Dim test As String = ""
ret = NetWkstaUserGetInfo(Nothing, 1, pwk1)
RtlMoveMemory(wk1, pwk1, Len(wk1))
ret = NetApiBufferFree(pwk1)
Return "test"
End Function
When I put a break point in here, I see that after RtlMoveMemory, my wk1 contains a pointer to a username, and all the others are 0. This is consistant. Now, if I change Dim test As String = "" to Dim login As String = "" and run it again, wk1 contains pointers to both username and logon_domain.
If I change it to Dim login As String, it contains a pointer to only the username. Depending on what I change that (completely unused) variable name to, I get a different result. How is this possible?
I have always been under the impression that it cannot possibly matter what you choose to name your variable. And that declaring a variable, and then never using it, cannot possible be different from not having a variable there at all.
I’ve tried this on 2 computers, with consistant results (one on .Net 3.5 and one on 4.0). When I tried converting it to C#, however, I was not able to reproduce it.
I am aware, by the way, that I can just use System.Environment to get what I need about the current user information; this was old VB6 code that was automatically upgraded (and I’ve edited it a little to make it more trivial). I’m just trying to understand how this behavior is possible. Obviously I’ve spent many years assuming something about .Net that isn’t quite true.
Solution, with help from Raymond Chen:
The declaration for RtlMoveMemory should have src passed as ByVal, instead of as ByRef. Because the src that I’m passing is a pointer to a memory location, passing it ByRef caused it to pass the memory address of where my pointer was stored… in other words, the location of my variable declaration. Thus, having other variables or differently named variables around produced different results.
A note as to why it was being passed ByRef… in the original VB6 code, it didn’t specify ByVal in the declaration, and ByRef is the default in VB6. So, the declaration itself was expecting ByRef. However, the call to the API specified that it was passing the variable ByVal. This is something that you cannot do in .Net; you have to pass it the same as the declaration is expecting. So the auto-upgrader added ByRef to the declaration, which does keep the declaration the same as VB6, but it ignored the ByVal in the call.