当您的代码是本机 Win32 应用程序访问的 COM 对象AppDomain
(默认Evidence
为空)时,或者当它在 PowerShell.exe 的命令行版本中加载并运行时,也会发生类似的情况。当办公文档超过特定文件大小时,我在使用使用 IsolatedStorage的 OpenXML(特别是EPPlus )程序集时遇到了这个问题。
与其在默认的内部旋转另一个AppDomain
并处理额外级别的编组/远程处理,我更喜欢使用反射来处理当前AppDomain
的证据。
这是 C# 中的概念证明:
using System;
namespace AppDomainEvidence
{
class Program
{
static void Main(string[] args)
{
var initialAppDomainEvidence = System.Threading.Thread.GetDomain().Evidence; // Setting a breakpoint here will let you inspect the current AppDomain's evidence
try
{
var usfdAttempt1 = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForDomain(); // This will fail when the current AppDomain Evidence is instantiated via COM or in PowerShell
}
catch (Exception e)
{
// Set breakpoint here to inspect Exception "e"
}
// Create a new Evidence that include the MyComputer zone
var replacementEvidence = new System.Security.Policy.Evidence();
replacementEvidence.AddHostEvidence(new System.Security.Policy.Zone(System.Security.SecurityZone.MyComputer));
// Replace the current AppDomain's evidence using reflection
var currentAppDomain = System.Threading.Thread.GetDomain();
var securityIdentityField = currentAppDomain.GetType().GetField("_SecurityIdentity", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
securityIdentityField.SetValue(currentAppDomain,replacementEvidence);
var latestAppDomainEvidence = System.Threading.Thread.GetDomain().Evidence; // Setting a breakpoint here will let you inspect the current AppDomain's evidence
var usfdAttempt2 = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForDomain(); // This should work
}
}
}
这是我在 PowerShell 中实现的解决方法:
# This one will fail
$usfd = [System.IO.IsolatedStorage.IsolatedStorageFile]::GetUserStoreForDomain()
# Inspect the current AppDomain's Evidence
[threading.thread]::GetDomain().Evidence
# Modify the current AppDomain's Evidence
$evidence = new-object System.Security.Policy.Evidence
$zone = new-object System.Security.Policy.Zone('MyComputer')
$evidence.AddHost($zone)
$currentAppDomain = [threading.thread]::GetDomain()
$securityIdentityField=$currentAppDomain.GetType().GetField('_SecurityIdentity','Instance,NonPublic')
$securityIdentityField.SetValue($currentAppDomain, $evidence)
# Inspect the current AppDomain's Evidence
[threading.thread]::GetDomain().Evidence
# This one will succeed
$usfd = [System.IO.IsolatedStorage.IsolatedStorageFile]::GetUserStoreForDomain()