代码之家  ›  专栏  ›  技术社区  ›  Adam Driscoll

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

  •  4
  • Adam Driscoll  · 技术社区  · 14 年前

    此代码导致以下异常, :

    “试图读或写保护 另一个内存已损坏“

    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次调用这个方法的1次调用中。一旦它开始发生,之后的每个调用都会抛出异常。重新启动进程会导致错误消失。

    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 回复  |  直到 14 年前
        1
  •  3
  •   Abel    14 年前

    我的猜测是,这个错误是由于没有正确释放资源造成的,就像您的情况一样。我可能错了,因为这是一个原因,但这可能是一个好主意,包装的原因 FreeHGlobal CloseHandle in a finally block ,以确保正确清理。

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

    编辑(编辑后)

    问题很可能出在必要的地方 SizeConst 大小成本 价值观。如果 大于所需大小,则 Marshal.PtrToStructure 大小成本 ,此内存可以访问,也可以不访问。

    AllocHGlobal 呼叫是 至少是整个结构的大小 . 例如,尝试添加4000,看看是否会返回错误(存在其他更整洁的解决方案,但让我们暂时保持简单)。