托管代码调用非托管代码导致访问冲突...有时候

4

这段代码有时会引发以下异常:

"尝试读取或写入受保护的内存。这通常是其他内存已损坏的指示"

private static TOKEN_GROUPS GetTokenGroups(IntPtr tokenHandle)
{
    var groups = new TOKEN_GROUPS();
    uint tokenInfoLength = 0;
    uint returnLength;

    var res = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenGroups, IntPtr.Zero,
                                  tokenInfoLength, out returnLength);

    if (!res && returnLength > 0)
    {
        tokenInfoLength = returnLength;
        var tokenInfo = Marshal.AllocHGlobal((int) tokenInfoLength);
        res = GetTokenInformation(tokenHandle,
                                  TOKEN_INFORMATION_CLASS.TokenGroups,
                                  tokenInfo,
                                  tokenInfoLength,
                                  out returnLength);
        if(res)
        {
            groups = (TOKEN_GROUPS)Marshal.PtrToStructure(tokenInfo, typeof (TOKEN_GROUPS));
        }

        Marshal.FreeHGlobal(tokenInfo);
        CloseHandle(tokenHandle);
    }
    else
    {
        var error = new Win32Exception(Marshal.GetLastWin32Error());
        _log.WarnFormat("Failed evaluate the call to get process token information. {0}", error.Message);
    }
    return groups;
}

出错的代码行是groups = (TOKEN_GROUPS)Marshal.PtrToStructure(tokenInfo, typeof (TOKEN_GROUPS));我会说异常在每20次调用此方法时发生一次。一旦开始发生,之后的每次调用都会抛出异常。重新启动进程会使错误消失。

IntPtr tokenHandle的结果是:

var processId = GetCurrentProcess();

            _log.InfoFormat("Process ID [{0}]", processId.ToString());

            if (processId != IntPtr.Zero)
            {
                IntPtr tokenHandle;
                if (OpenProcessToken(processId, TOKEN_READ, out tokenHandle))
                {
                    groups = GetTokenGroups(tokenHandle);
                }

编辑 希望这不会是信息过载,以下是pinvoke声明:

     struct TOKEN_GROUPS
    {
        public uint GroupCount;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4000)]
        public SID_AND_ATTRIBUTES[] Groups;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct SID_AND_ATTRIBUTES
    {
        public IntPtr SID;
        public uint Attributes;
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool GetTokenInformation(
        IntPtr TokenHandle,
        TOKEN_INFORMATION_CLASS TokenInformationClass,
        IntPtr TokenInformation,
        uint TokenInformationLength,
        out uint ReturnLength);

    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool OpenProcessToken(IntPtr ProcessHandle,
        UInt32 DesiredAccess, out IntPtr TokenHandle);

    [DllImport("kernel32.dll")]
    static extern IntPtr GetCurrentProcess();

    enum TOKEN_INFORMATION_CLASS
    {
        TokenUser = 1,
        TokenGroups,
        TokenPrivileges,
        TokenOwner,
        TokenPrimaryGroup,
        TokenDefaultDacl,
        TokenSource,
        TokenType,
        TokenImpersonationLevel,
        TokenStatistics,
        TokenRestrictedSids,
        TokenSessionId,
        TokenGroupsAndPrivileges,
        TokenSessionReference,
        TokenSandBoxInert,
        TokenAuditPolicy,
        TokenOrigin
    }
1个回答

3
我的猜测是错误是由于资源没有正确释放导致的,这在你的情况下是可能的原因。我可能错误地认为这是原因,但最好将 FreeHGlobalCloseHandle 放到 finally中,以确保正确清理。
如果错误仍然存在,则可能是其他问题(结构不正确或声明中的数据布局不正确TOKEN_GROUPS 的 LayoutKind 不正确?)或对此特定 API 的错误使用(我对此不太熟悉)。
编辑(在您的编辑之后):
问题很可能在所需的 SizeConst 属性参数上。考虑以下内容:GetTokenInformation 已经给出了 tokeInfoLength 中返回的大小。您进行了分配。该大小不太可能等于 SizeConst 值。如果 SizeConst 大于所需大小,则 Marshal.PtrToStructure 将访问超出您分配的长度,因为它只知道 SizeConst,而且这个内存可能是可访问的,可能不是。
要解决此问题,请确保 AllocHGlobal 调用 至少为整个结构作为 marshal 的大小。例如,尝试添加 4000,看看是否返回错误(还有其他更简洁的解决方案,但让我们暂且保持简单)。

好的,我会把这些调用移到正确的位置。无论如何,良好的代码整理都是必要的;)。谢谢你的建议。 - Adam Driscoll
我已经尝试了8000、16000和20000的值,但错误仍然存在 :| - Adam Driscoll
@Adam Driscoll:你是在AllocHGlobal还是SizeConst中尝试它们?应该在AllocHGlobal中。但是等一下,我会稍微试一下。 - Abel
起初,我尝试将其放在SizeConst中,但最终失败了。我将AllocHGlobal设为4000,但在第一次执行时就出现了问题。 - Adam Driscoll
注意:如果SizeConst中的数字小于tokenInfoLength,那么实际上并没有什么问题,除了您将无法使用PtrToStructure填充整个数组。 - Abel
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接