1

我有一些 IO 代码可以读取 try..catch 中的流。它捕获 IOException 并在捕获中调用System.Runtime.InteropServices.Marshal.GetHRForException() ,以尝试根据 HResult 采取不同的操作。像这样的东西:

try 
{
    stream.Read(...);
}
catch (IOException ioexc1)
{
   uint hr = (uint) Marshal.GetHRForException(ioexc1);
   if (hr == ...) 
      do_one_thing();
   else
      do_another();
}

该程序集使用AllowPartiallyTrustedCallersAttribute进行签名和标记。

但是在 ASP.NET 中使用 trust="medium" 运行这段代码,我得到了这个异常:

Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.

几个问题:

  1. 我认为发生异常是因为 GetHRForException 调用了非托管代码,这在中等信任中是不允许的。正确的?
  2. 抛出此异常,不是在执行 GetHRForException 时,而是在方法被 JIT 处理时 - 对吗?(堆栈跟踪显示了我的方法,但我 99% 确定没有发生 IO 异常)
  3. 如果是这样,有没有办法让我改变部分信任环境中的行为,这样我就不会在不允许的地方调用 GetHRForException (非托管代码)?换句话说,如何让 JIT 在编译时成功,同时在运行时评估代码是否应该调用 GetHRForException()?像这样的东西:

catch (IOException ioexc1)
{
   if (!OkToCallUnmanaged())
       throw ioexc1; 

   uint hr = (uint) Marshal.GetHRForException(ioexc1);
   if (hr == ...) 
      do_one_thing();
   else
      do_another();
}

我认为有一个运行时机制来测试权限是否可用,但一直找不到。


编辑这篇博客文章是答案吗?微软的 ShawnFa 说你不能在一个受 LinkDemand 保护的方法周围做一个 try ... catch(SecurityException)。如果 MethodA() 调用 MethodB(),并且 MethodB() 用 LinkDemand 标记为完全信任,则使用 MethodA 检查 LinkDemand is Jit'ed。因此,为了避免 SecurityException,我需要将 Marshal.GetHRForException 提取到一个单独的方法中。那是对的吗?

应用于我的代码,MethodA() 可能是调用 Read 的代码,然后在 catch 中尝试调用 GetHRForException()。GetHRForException 是 MethodB()。当 MethodA() 为 JIT 时评估 LinkDemand。(此 LinkDemand 在我的中等信任 ASP.NET 方案中失败)。如果我将 GetHRForException 移动到一个新方法 MethodC() 中,并且仅在命令式 permission.Demand() 成功后有条件地调用 MethodC(),理论上我应该能够在 JIT 时间避免 SecurityException,因为 MethodC() 将只有在 permission.Demain() 成功后才会进行 JIT。

4

3 回答 3

3

所需的方法是SecurityPermission.IsUnrestricted()。它返回一个 true 或 false 指示是否允许该权限。它不需要权限SecurityPermission.Demand()也是如此。我使用 IsUnrestricted 和 SecurityPermissionFlag.UnmanagedCode 来查看是否允许程序集调用非托管代码,然后仅在允许时才调用非托管代码。

还有一个额外的转折。JIT 编译器在编译方法时,会在任何调用我要编译的方法的方法上检查 CodeAccessPermission LinkDemands。Marshal.GetHRForException() 用 LinkDemand 标记。因此,我调用 Marshal.GetHRForException() 的方法将在 JIT 编译时抛出无法捕获的 SecurityException,当在受限环境中运行时,例如具有中等信任度的 ASP.NET。因此,在这种情况下,我们绝不能 JIT 调用 Marshal.GetHRForException() 的方法,这意味着我需要在我的代码中将 Marshal.GetHRForException() 分解为一个单独的方法,该方法仅在 UnmanagedCode 为不受限制。

这是一些示例代码:

internal void DoTheThing()
{
    try
    {
        DoSomethingThatMayCauseAnException();
    }
    catch (System.IO.IOException ioexc1)
    {
        // Check if we can call GetHRForException, 
        // which makes unmanaged code calls.
        var p = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
        if (p.IsUnrestricted())
        {
            uint hresult = _HRForException(ioexc1);
            if (hresult == 0x80070021)  // ERROR_LOCK_VIOLATION
                TakeActionOnLockViolation();  // maybe notify the user
            else
                throw new Exception("Cannot handle", ioexc1);
        }
        else
        {
            // The permission is restricted. Therefore, we cannot call
            // GetHRForException, and cannot do the subtle handling of
            // ERROR_LOCK_VIOLATION.  Just bail.
            throw new Exception("Cannot handle", ioexc1);
        }
    }
}

// This method must remain separate, and must not be marked with a LinkDemand for
// UnmanagedCode.
//
// Marshal.GetHRForException() is needed to do special exception handling for
// the read.  But, that method requires UnmanagedCode permissions, and is marked
// with LinkDemand for UnmanagedCode.  In an ASP.NET medium trust environment,
// where UnmanagedCode is restricted, will generate a SecurityException at the
// time of JIT of the method that calls a method that is marked with LinkDemand
// for UnmanagedCode. The SecurityException, if it is restricted, will occur
// when this method is JITed.
//
// The Marshal.GetHRForException() is factored out of DoTheThing in order to
// avoid the SecurityException at JIT compile time. Because _HRForException is
// called only when the UnmanagedCode is allowed, .NET never
// JIT-compiles this method when UnmanagedCode is disallowed, and thus never
// generates the JIT-compile time exception.
//
private static uint _HRForException(System.Exception ex1)
{
    return unchecked((uint)System.Runtime.InteropServices.Marshal.GetHRForException(ex1));
}
于 2009-07-20T17:56:38.403 回答
1
  1. 是 - 中等信任不允许调用非托管代码。唯一允许它的信任级别是完全信任。
  2. 这取决于。CAS 要求可以在运行时发生,但托管环境也可以四处游荡,寻找它不能做的事情。
  3. 您可以通过使用带有SecurityPermission实例的 CAS 需求来测试是否可以调用非托管代码。

生成 CAS 需求的代码如下所示

SecurityPermission permission = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
try
{
  permission.Demand();
  // Got it, away we go
}
catch (SecurityException)
{
  // Could not get permission to make unmanaged code calls
  // React accordingly.
}
于 2009-07-12T14:32:50.823 回答
0

System.Runtime.InteropServices.Marshal.GetHRForException()是否可以通过检查异常类型获得任何您无法获得的东西?看起来它使用静态映射。

于 2009-07-13T01:40:03.103 回答