5

我想了解为什么我的代码中会出现两个 CA 警告:

CA2000在失去范围之前处置对象 在方法“ImpersonateValidUser(string, string, string, LogOnType, LogOnProvider, ImpersonationLevel)”中,在对对象“tempWindowsIdentity”的所有引用超出范围之前调用 System.IDisposable.Dispose。

CA1404在调用 GetLastWin32Error的 P/Invoke 方法“ImpersonateValidUser(string, string, string, LogOnType, LogOnProvider, ImpersonationLevel)”之后立即调用 GetLastError,但之前对“IDisposable.Dispose()”的调用不是 P/Invoke 语句。将调用移至 GetLastWin32Error,使其紧跟相关平台调用调用。

下面是简化的代码示例,在警告出现的地方精确地注释了行:

private static void Impersonate(string userName, string domain, string password, LogOnType logonType, LogOnProvider logonProvider, ImpersonationLevel impersonationLevel)
{
    var token = IntPtr.Zero;
    var tokenDuplicate = IntPtr.Zero;
    if (NativeMethods.RevertToSelf())
    {
        if (NativeMethods.LogonUser(userName, domain, password, (int)logonType, (int)logonProvider, ref token) != 0)
        {
            if (NativeMethods.DuplicateToken(token, (int)impersonationLevel, ref tokenDuplicate) != 0)
            {
                /* CA2000 */ using (var tempWindowsIdentity = new WindowsIdentity(tokenDuplicate))
                {
                    _impersonationContext = tempWindowsIdentity.Impersonate();
                    return;
                }
            }
        }
    }
    /* CA1404 */ var e = Marshal.GetLastWin32Error();
    throw new Win32Exception(e);
}

CA2000 -语句完成tempWindowsIdentity后未使用对象using(),为什么会出现此警告?

CA1404 -Marshal.GetLastWin32Error()总是在本机方法之后调用。即使using()之前调用了情况语句,该return子句也会出现,因此Marshal.GetLastWin32Error()不会被调用,那么为什么会出现这个警告?

4

3 回答 3

0

我相信 CA2000 发生是因为您违反了规则的以下部分,这意味着您正在创建一个一次性对象 insideusing语句;

返回一次性对象需要在 using 块之外的 try/finally 块中构造该对象。

CA1404 也会发生,因为您没有Marshal.GetLastWin32Error();在本地调用后立即调用。尝试将Marshal.GetLastWin32Error();每个if语句作为代码中的第一项调用。

于 2013-01-22T10:59:18.797 回答
0

这段代码应该修复那些 CA 警告......不是很优雅......但代码分析永远不会产生优雅的代码。

private static void Impersonate(string userName, string domain, string password, LogOnType logonType, LogOnProvider logonProvider, ImpersonationLevel impersonationLevel)
{
    if (!NativeMethods.RevertToSelf())
        throw new Win32Exception(Marshal.GetLastWin32Error());

    IntPtr token = IntPtr.Zero;

    if (NativeMethods.LogonUser(userName, domain, password, (int)logonType, (int)logonProvider, ref token) == 0)
        throw new Win32Exception(Marshal.GetLastWin32Error());

    IntPtr tokenDuplicate = IntPtr.Zero;

    if (NativeMethods.DuplicateToken(token, (int)impersonationLevel, ref tokenDuplicate) == 0)
        throw new Win32Exception(Marshal.GetLastWin32Error());

WindowsIdentity tempWindowsIdentity;

    try
    {
        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
        _impersonationContext = tempWindowsIdentity.Impersonate();
    }
    finally
    {
        if (tempPort != null)
        {
            tempWindowsIdentity.Dispose();
            tempWindowsIdentity = null;
        }
    }
}

对于 CA1404 的问题,如果您不立即捕获它,错误的 API 调用引发的错误代码可能会同时被来自其他托管类库方法的内部调用覆盖。在 CA2000 中,您的方法中的 WindowsIdentity 已创建,但在对对象的所有引用都超出范围之前不会释放该对象;这样做你不允许在故障点引发异常。

于 2013-01-22T10:54:01.143 回答
-1

我们通过以下方式解决了 CA1404。原因是“!= 0”是 LogonUser() 或 DuplicateToken() 之后的立即调用。

private static void Impersonate(string userName, string domain, string password, LogOnType logonType, LogOnProvider logonProvider, ImpersonationLevel impersonationLevel)
{
    var token = IntPtr.Zero;
    var tokenDuplicate = IntPtr.Zero;
    if (NativeMethods.RevertToSelf())
    {
        var logonUserSuccessful = NativeMethods.LogonUser(userName, domain, password, (int) logonType, (int) logonProvider, ref token);
        var e = Marshal.GetLastWin32Error();  // call before comparison against 0 to avoid CA1404
        if (logonUserSuccessful != 0)
        {
            var duplicateTokenSuccessful = NativeMethods.DuplicateToken(token, (int) impersonationLevel, ref tokenDuplicate);
            e = Marshal.GetLastWin32Error();  // call before comparison against 0 to avoid CA1404
            if (duplicateTokenSuccessful != 0)
            {
                /* CA2000 */
                using (var tempWindowsIdentity = new WindowsIdentity(tokenDuplicate))
                {
                    _impersonationContext = tempWindowsIdentity.Impersonate();
                    return;
                }
            }
        }
    }
    throw new Win32Exception(e);
}
于 2014-11-17T09:41:42.697 回答