Marshal.PtrToStructure 在 64 位上崩溃
Marshal.PtrToStructure crashing on 64bit
当 运行 在 64 位中时,我的单元测试在这段代码上崩溃。
崩溃发生在循环的第 2 次迭代的 Marshal.PtrToStructure 调用中。 "entriesRead" 说 4 所以它应该能够正确读取,但它没有。 Marshal.SizeOf(typeof(WinAPI.NETAPI32.USER_INFO_4)) 在 64 位中为 192 字节。这是错误的来源吗?
....
try {
int entriesRead;
int totalEntries;
int resumeHandle;
var result = WinAPI.NETAPI32.NetUserEnum(
this.NTCompatibleHostName,
3,
2,
out bufPtr,
-1,
out entriesRead,
out totalEntries,
out resumeHandle
);
if (result != 0) {
throw new NetApiException(
result,
"Failed to enumerate local users on host '{0}'",
Host
);
}
var structSize = Marshal.SizeOf(typeof(WinAPI.NETAPI32.USER_INFO_4));
var startAddr = bufPtr.ToInt64();
var endAddr = startAddr + entriesRead * structSize;
for (var offset = startAddr; offset < endAddr; offset += structSize) {
var userInfo =
(WinAPI.NETAPI32.USER_INFO_4)Marshal.PtrToStructure(
new IntPtr(offset),
typeof(WinAPI.NETAPI32.USER_INFO_4)
);
}
} catch (Exception error) {
}
[StructLayout(LayoutKind.Sequential)]
public struct USER_INFO_4 {
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_name;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_password;
public uint usri4_password_age;
public uint usri4_priv;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_home_dir;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_comment;
public uint usri4_flags;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_script_path;
public uint usri4_auth_flags;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_full_name;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_usr_comment;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_parms;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_workstations;
public uint usri4_last_logon;
public uint usri4_last_logoff;
public uint usri4_acct_expires;
public uint usri4_max_storage;
public uint usri4_units_per_week;
public IntPtr usri4_logon_hours;
public uint usri4_bad_pw_count;
public uint usri4_num_logons;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_logon_server;
public uint usri4_country_code;
public uint usri4_code_page;
public IntPtr usri4_user_sid;
public uint usri4_primary_group_id;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_profile;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_home_dir_drive;
public uint usri4_password_expired;
}
[DllImport("netapi32.dll")]
[return: MarshalAs(UnmanagedType.U4)]
public static extern NET_API_STATUS NetUserEnum([MarshalAs(UnmanagedType.LPWStr)] string servername, int level, int filter, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out int resume_handle);
结构翻译正确。它的大小是正确的。您对函数调用的翻译是正确的。
问题是您正在通过第 3 级。这意味着函数 returns USER_INFO_3
而不是 USER_INFO_4
。 NetUserEnum
的文档完全没有提及它曾经返回 USER_INFO_4
值。为了获得 USER_INFO_4
值,您必须调用 NetUserGetInfo
.
调用 NetUserEnum
传递服务器名称和级别值 0
。这将枚举用户名。然后将这些用户名中的每一个连同服务器名称一起传递给级别为 4 的 NetUserGetInfo
。
当 运行 在 64 位中时,我的单元测试在这段代码上崩溃。
崩溃发生在循环的第 2 次迭代的 Marshal.PtrToStructure 调用中。 "entriesRead" 说 4 所以它应该能够正确读取,但它没有。 Marshal.SizeOf(typeof(WinAPI.NETAPI32.USER_INFO_4)) 在 64 位中为 192 字节。这是错误的来源吗?
....
try {
int entriesRead;
int totalEntries;
int resumeHandle;
var result = WinAPI.NETAPI32.NetUserEnum(
this.NTCompatibleHostName,
3,
2,
out bufPtr,
-1,
out entriesRead,
out totalEntries,
out resumeHandle
);
if (result != 0) {
throw new NetApiException(
result,
"Failed to enumerate local users on host '{0}'",
Host
);
}
var structSize = Marshal.SizeOf(typeof(WinAPI.NETAPI32.USER_INFO_4));
var startAddr = bufPtr.ToInt64();
var endAddr = startAddr + entriesRead * structSize;
for (var offset = startAddr; offset < endAddr; offset += structSize) {
var userInfo =
(WinAPI.NETAPI32.USER_INFO_4)Marshal.PtrToStructure(
new IntPtr(offset),
typeof(WinAPI.NETAPI32.USER_INFO_4)
);
}
} catch (Exception error) {
}
[StructLayout(LayoutKind.Sequential)]
public struct USER_INFO_4 {
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_name;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_password;
public uint usri4_password_age;
public uint usri4_priv;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_home_dir;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_comment;
public uint usri4_flags;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_script_path;
public uint usri4_auth_flags;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_full_name;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_usr_comment;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_parms;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_workstations;
public uint usri4_last_logon;
public uint usri4_last_logoff;
public uint usri4_acct_expires;
public uint usri4_max_storage;
public uint usri4_units_per_week;
public IntPtr usri4_logon_hours;
public uint usri4_bad_pw_count;
public uint usri4_num_logons;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_logon_server;
public uint usri4_country_code;
public uint usri4_code_page;
public IntPtr usri4_user_sid;
public uint usri4_primary_group_id;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_profile;
[MarshalAs(UnmanagedType.LPWStr)]
public string usri4_home_dir_drive;
public uint usri4_password_expired;
}
[DllImport("netapi32.dll")]
[return: MarshalAs(UnmanagedType.U4)]
public static extern NET_API_STATUS NetUserEnum([MarshalAs(UnmanagedType.LPWStr)] string servername, int level, int filter, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out int resume_handle);
结构翻译正确。它的大小是正确的。您对函数调用的翻译是正确的。
问题是您正在通过第 3 级。这意味着函数 returns USER_INFO_3
而不是 USER_INFO_4
。 NetUserEnum
的文档完全没有提及它曾经返回 USER_INFO_4
值。为了获得 USER_INFO_4
值,您必须调用 NetUserGetInfo
.
调用 NetUserEnum
传递服务器名称和级别值 0
。这将枚举用户名。然后将这些用户名中的每一个连同服务器名称一起传递给级别为 4 的 NetUserGetInfo
。