3

我对 .Net 编程的了解都告诉我,我在这里看到的行为是完全不可能的。那么有人可以解释一下这里发生了什么吗?简单代码:

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

当我在这里放一个断点时,我看到在 RtlMoveMemory 之后,我的 wk1 包含一个指向用户名的指针,而其他所有的都是 0。这是一致的。现在,如果我更改Dim test As String = ""Dim login As String = ""并再次运行它,wk1 包含指向用户名和登录域的指针。

如果我将其更改为Dim login As String,它包含一个仅指向用户名的指针。根据我将该(完全未使用的)变量名称更改为的内容,我会得到不同的结果。这怎么可能?

我一直认为,你选择什么命名你的变量可能无关紧要。声明一个变量,然后从不使用它,不可能与根本没有变量不同。

我已经在 2 台计算机上尝试过,结果一致(一台在 .Net 3.5 上,另一台在 4.0 上)。但是,当我尝试将其转换为 C# 时,我无法重现它。

顺便说一句,我知道我可以使用 System.Environment 来获取我需要的有关当前用户信息的信息;这是自动升级的旧 VB6 代码(我对其进行了一些编辑以使其更简单)。我只是想了解这种行为是如何发生的。显然,我花了很多年时间假设关于 .Net 的一些事情并不完全正确。

4

1 回答 1

1

解决方案,在 Raymond Chen 的帮助下:

RtlMoveMemory 的声明应该将 src 作为 ByVal 传递,而不是作为 ByRef。因为我传递的 src 是一个指向内存位置的指针,所以通过 ByRef 传递它会导致它传递存储我的指针的内存地址......换句话说,我的变量声明的位置。因此,周围有其他变量或不同命名的变量会产生不同的结果。

关于为什么在原始 VB6 代码中传递 ByRef... 的注释,它没有在声明中指定 ByVal,而 ByRef 是 VB6 中的默认值。因此,声明本身期待 ByRef。但是,对 API 的调用指定它正在传递变量 ByVal。这是您在 .Net 中无法做到的;你必须像声明所期望的那样传递它。因此,自动升级程序将 ByRef 添加到声明中,这确实使声明与 VB6 相同,但它忽略了调用中的 ByVal。

于 2012-05-22T17:07:09.083 回答