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

时间:2010-07-26 19:31:42

标签: c# pinvoke marshalling unmanaged managed

此代码导致以下异常,有时

  

“尝试读取或写入受保护的   记忆。这通常是一个迹象   其他记忆是腐败的“

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));我会说异常发生在1和每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 个答案:

答案 0 :(得分:3)

我的猜测是,错误是由于未正确释放资源造成的,就像您的情况一样。我可能错了,但是包裹FreeHGlobalCloseHandle in a finally block可能是个好主意,以确保正确清理。

如果错误仍然存​​在,则可能是其他内容(错误的结构或wrong data layout in the declaration或错误LayoutKind for TOKEN_GROUPS?)或错误使用此特定API(我不太熟悉)。

编辑(编辑后)

问题可能在于必要的SizeConst属性参数。请考虑以下事项:GetTokenInformation已经给出了tokeInfoLength中返回的大小。你分配这个。此大小不太可能等于SizeConst值。如果SizeConst大于所需大小,则Marshal.PtrToStructure将进一步访问您分配的长度,因为它只知道SizeConst,并且此内存可能是可访问的,并且可能不是

要解决此问题,请确保AllocHGlobal调用至少是整个结构的大小。例如,尝试添加4000并查看错误是否返回(其他更简洁的解决方案存在,但让我们暂时保持简单)。