I'm attempting to create an open source library that spawn a new AppDomain
and runs a PowerShell
script in it. I have a static method that takes the name of the powershell file and the name of the AppDomain
. The method executes successfully when called from a C# console app, but not PowerShell
.
I know the dll is being loaded in the second app domain because of this entry in the fusionlog.
The class declaraton and constructor looks like this.
public class AppDomainPoshRunner : MarshalByRefObject{
public AppDomainPoshRunner (){
Console.WriteLine("Made it here.");
}
}
That message in the constructor gets output when I call CreateInstanceFromAndUnwrap whether I run the dll from a C# console app or from the PowerShell app.
The failure occurs when I cast the value returned by CreateInstanceFromAndUnwrap
to AppDomainPoshRunner in the static method below.
public static string[] RunScriptInAppDomain(string fileName, string appDomainName = "Unamed")
{
var assembly = Assembly.GetExecutingAssembly();
var setupInfo = new AppDomainSetup
{
ApplicationName = appDomainName,
// TODO: Perhaps we should setup an even handler to reload the AppDomain similar to ASP.NET in IIS.
ShadowCopyFiles = "true"
};
var appDomain = AppDomain.CreateDomain(string.Format("AppDomainPoshRunner-{0}", appDomainName), null, setupInfo);
try {
var runner = appDomain.CreateInstanceFromAndUnwrap(assembly.Location, typeof(AppDomainPoshRunner).FullName);
if (RemotingServices.IsTransparentProxy(runner))
Console.WriteLine("The unwrapped object is a proxy.");
else
Console.WriteLine("The unwrapped object is not a proxy!");
Console.WriteLine("The unwrapped project is a {0}", runner.GetType().FullName);
/* This is where the error happens */
return ((AppDomainPoshRunner)runner).RunScript(fileName);
}
finally
{
AppDomain.Unload(appDomain);
}
}
When running that in PowerShell I get an InvalidCastExcception
with the message Unable to cast transparent proxy to type JustAProgrammer.ADPR.AppDomainPoshRunner
.
What am I doing wrong?
I had the same problem: I created sandbox with Execute only permissions (the min one can have) to execute untrusted code in very restricted environment. All worked great in C# application, but did not work (the same cast exception) when starting point was vbs script creating .NET COM object. I think PowerShell also uses COM. I found workaround using AppDomain.DoCallBack, which avoids getting proxy from the appdomain. This is the code. If you find a better option, please post. Registering in GAC is not a good solution for me...
class Test
{
/*
create appdomain as usually
*/
public static object Execute(AppDomain appDomain, Type type, string method, params object[] parameters)
{
var call = new CallObject(type, method, parameters);
appDomain.DoCallBack(call.Execute);
return call.GetResult();
}
}
[Serializable]
public class CallObject
{
internal CallObject(Type type, string method, object[] parameters)
{
this.type = type;
this.method = method;
this.parameters = parameters;
}
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
public void Execute()
{
object instance = Activator.CreateInstance(this.type);
MethodInfo target = this.type.GetMethod(this.method);
this.result.Data = target.Invoke(instance, this.parameters);
}
internal object GetResult()
{
return result.Data;
}
private readonly string method;
private readonly object[] parameters;
private readonly Type type;
private readonly CallResult result = new CallResult();
private class CallResult : MarshalByRefObject
{
internal object Data { get; set; }
}
}
It sounds very much like a loading context issue. Type identity isn't just about the physical assembly file; it's also about how and where it was loaded. Here's an old blog post from Suzanne Cook that you'll probably have to read fifteen times until you can begin to make sense of your problem.
Choosing a Binding Context
https://learn.microsoft.com/en-us/archive/blogs/suzcook/choosing-a-binding-context
Before you say "but it works in a console app," remember that when running it from powershell, you have an entirely different kettle of fish as regards the calling appdomain's context, probing paths, identity etc.
Good luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With