Say I have the following class hierarchy defined in a class library:
public interface IFoo
{
string GetMsg();
}
public abstract class FooBase
{
public virtual string GetMsg()
{
return "Foobase msg";
}
}
public class Foo : FooBase, IFoo
{
#region IFoo Members
public new string GetMsg()
{
return base.GetMsg();
}
#endregion
}
I'm consuming this assembly in a console application and using reflection I'm creating an instance of the Foo class typed to IFoo as follows:
Assembly a = Assembly.LoadFile(Path.GetFullPath("TestClassLib.dll"));
var typeDef = a.GetType("TestClassLib.Foo");
var fooInst = Activator.CreateInstance(typeDef) as IFoo;
string msg = fooInst.GetMsg();
The above works fine.
Now if I take this code and port it to an ASP.NET Web web page like so:
namespace TestWebApp
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string filePath = Server.MapPath(#"~/bin/TestClassLib.dll");
Assembly a = Assembly.LoadFile(Path.GetFullPath(filePath));
var typeDef = a.GetType("TestClassLib.Foo");
var fooInst = Activator.CreateInstance(typeDef) as IFoo;
string msg = fooInst.GetMsg();
}
}
}
fooInst is null on the following line:
var fooInst = Activator.CreateInstance(typeDef) as IFoo;
What's interesting is when I debug the web page, I have a valid type definition in 'typeDef' variable and what is weird is that if I add Activator.CreateInstance(typeDef) as IFoo
to the Watch window in Visual Studio the result is not null!
What am I missing here?
P.S. - I've already verified that the assembly(TestClassLib.dll) is present in the bin directory of the ASP.NET app.
My guess is that you're getting the object back from Activator.CreateInstance, but the defensive cast ("as IFoo") is failing, possibly due to load context (since you loaded the assembly with LoadFrom- see here). Is IFoo perchance defined in both the calling assembly and the one you're loading dynamically? Maybe try letting Fusion load the assembly by doing a Type.GetType() with an assembly-qualified type name to see if you get the same result (also try storing in an object ref without the "as" to make sure CreateInstance is giving you something back).
As far as I know, you can only use as when the class/type you are casting to has a constructor. Obviously, an interface does not have a constructor. Therefore, you must box in the normal style:
var fooInst = (IFoo)Activator.CreateInstance(typeDef);
I'm pretty sure this is the case, but I could be wrong.
Related
I am facing with a problem when loading dynamically a library with a shared reference. I understood where the problem could be, but I don't really know how I can fix it.
I will try to explain my problem better, this is my structure:
I have 3 assemblies
DataProviderAssembly
ContractsAppAssembly
AppAssembly
I start saying that I can not currently use MEF.
The "DataProviderAssembly" loads dinamically the "AppAssembly" because it has a type that implements the IApp interface from the "ContractsAssembly".
Following the code below, when I try to call the method OnSignRequest from the App class, I get a MissingMethodException. Seems that the method were not found, but, if I remove the "RestRequest" parameter, everything works fine.
I think that, the dynamic loading of that DLL miss some reference information for the dependent assemblies.
**ContractsAppAssembly**
public interface IApp {
void GetData();
}
public abstract class MainClassBase : IApp {
public virtual void GetData(){
//Do Stuff
}
protected void OnSignRequest(RestRequest request){
//calling sign request event
}
}
public abstract class SecondClassBase : MainClassBase {
// i need this because it overrides other methods i didn't specify here
}
**AppAssembly**
public class App : SecondClassBase {
public override void GetData(){
RestRequest request = new RestRequest();
base.OnSignRequest(request);//<-- this throws a MissingMethodException
}
}
**DataProviderAssembly**
public class DataProvider{
public DataProvider{
string[] appsLibs = Directory.GetFiles(appsFolderPath, "*.dll", SearchOption.TopDirectoryOnly);
foreach (string appLib in appsLibs)
{
Assembly appAssemlby = Assembly.LoadFrom(appLib);
Type appType = providerAssemlby.GetTypes().FirstOrDefault(t => t.GetInterface(typeof(IApp).Name, true) != null && !t.IsAbstract);
if (appType == default(Type))
{
continue;
}
try
{
IApp app = (IApp )Activator.CreateInstance(appType);
initApp(app);
this.apps.Add(app.Name, app);
}catch(Exception ex)
{
}
}
}
}
All the assemblies reference RestSharp.dll, the build structure is the following:
Debug:
DataProviderAssembly.dll
Debug/Apps:
ContractsAppAssembly.dll
AppAssembly.dll
Obviously the DataProviderAssembly.dll has the "probing" option inside of the app.config file.
Debugging this code I cannot reach the "base.OnSignRequest" line of code inside of the App class , but, if I declare virtual the method "OnSignRequest" inside of the MainClassBase, the debugger let me reach that line of code giving me the exception only when I try to do a step on.
I want to use dynamic in C# to interact with objects defined in an external API (DLL). In such a way, I don't want to reference the API in my console app. Is this doable?
For instance, I used something as:
dynamic obj1 = new ObjectDefinedInAPI();
Compiler keeps on nagging that
The type or namespace name 'objectDefinedInAPI' could not be found ...
Any idea?
Thanks
You can load the assembly manually and then create a instance of a class given that you know the assembly name and class name.
var assembly = Assembly.LoadFrom("filepath");
var aClass = assembly.GetType("NameSpace.AClass");
dynamic instance = Activator.CreateInstance(aClass);
In addition to NeutronCode answer, you can also try the following:
public interface IDynamicService {
void DoSomething();
}
public void static main() {
var assembly = Assembly.LoadFrom("filepath");
var aClass = assembly.GetType("NameSpace.AClass");
IDynamicService instance = Activator.CreateInstance(aClass) as IDynamicService;
if (instance != null) {
instance.DoSomething();
}
}
This way you can ensure your type will have a specific implementation. Please note that the actual class must inherit the interface in order for this to work, so if you don't have access to the other dll source code, this won't help much. The upside of doing it this way is that you get Intellisense and will always be sure that your dynamic class interfaces well with your application.
I think I'm stuck with using .net 4.0 new security model. In general, I just want to load 3rd party assemblies into a sandbox. Sounds easy, right? Nevertheless...
I have 2 projects in my solution: CrossAppDomain.exe and UntrustedCode.dll.
In CrossAppdomain I created following base class for all my marshallables:
public abstract class Marshallable : MarshalByRefObject, IDisposable
{
[SecurityCritical]
public override object InitializeLifetimeService()
{
return null;
}
public void Dispose()
{
if (!RemotingServices.IsTransparentProxy(this))
{
RemotingServices.Disconnect(this);
}
}
}
And created base class for objects i will work with
public abstract class BaseClass : Marshallable
{
}
In UntrustedCode.dll I created derrived class
public class UntrustedClass : BaseClass
{
}
To create instance of UntrustedClass i use following factory:
public sealed class Factory
{
public BaseClass Create(string assName, string typeName)
{
var domain = CreateAppDomain();
ObjectHandle handle;
try
{
// This throws SecurityException with informational message "RequestFailed"
handle = domain.CreateInstance(typeof(AppDomainWorker).Assembly.FullName, typeof(AppDomainWorker).FullName);
}
catch (SecurityException)
{
// While this works fine...
handle = Activator.CreateInstanceFrom(domain,
typeof(AppDomainWorker).Assembly.ManifestModule.FullyQualifiedName,
typeof(AppDomainWorker).FullName);
}
var worker = (AppDomainWorker)handle.Unwrap();
worker.LoadAssemblies();
var obj = worker.Create(assName, typeName);
worker.Dispose();
return obj;
}
private AppDomain CreateAppDomain()
{
var name = Guid.NewGuid().ToString();
var permissions = new PermissionSet(PermissionState.None);
permissions.AddPermission(new SecurityPermission(PermissionState.Unrestricted));
permissions.AddPermission(new ReflectionPermission(PermissionState.Unrestricted));
permissions.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
var appSetup = new AppDomainSetup
{
ApplicationName = name,
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ShadowCopyDirectories = Path.GetFullPath(#"..\..\..\UntrustedCode\bin"),
ShadowCopyFiles = "true"
};
// Since Marshallable.InitializeLifetimeServices is overriden and marked with [SecurityCritical]
// we should add this assembly in full trusted list.
// Otherwise. TypeLoadException is thrown with message "Inheritance security rules violated while overriding member:
// 'CrossAppDomains.Marshallable.InitializeLifetimeService()'. Security accessibility of the overriding method must
// match the security accessibility of the method being overriden.
var sn = typeof (AppDomainWorker).Assembly.Evidence.GetHostEvidence<StrongName>();
var domain = AppDomain.CreateDomain(name, null, appSetup, permissions, sn);
return domain;
}
private sealed class AppDomainWorker : Marshallable
{
public BaseClass Create(string assName, string typeName)
{
var assembly = AppDomain.CurrentDomain.GetAssemblies()
.Single(a => assName.StartsWith(a.GetName().Name));
// Here TypeLoadException is thrown: Inheritance security rules violated by type: 'UntrustedCode.UntrustedClass'.
// Derived types must either match the security accessibility of the base type or be less accessible.
var obj = (BaseClass)Activator.CreateInstanceFrom(assembly.Location, typeName).Unwrap();
Debug.Assert(!RemotingServices.IsTransparentProxy(obj));
return obj;
}
public void LoadAssemblies()
{
var assemblyName = AssemblyName.GetAssemblyName(Path.GetFullPath(#"..\..\..\UntrustedCode\bin\Debug\UntrustedCode.dll"));
Assembly.Load(assemblyName);
}
}
}
And here where problem comes:
In Factory.Create() i successed creating AppDomainWorker class only when I used Activator.CreateInstance from. While more straightforward AppDomain.CreateInstanceAndUnwrap failed. This looks so unsteady, I mean this is either bug or security hole. But okay, the workaround works
In AppDomainWorker.Create() i get TypeLoadException: Inheritance security rules violated by type: 'UntrustedCode.UntrustedClass'. Derived types must either match the security accessibility of the base type or be less accessible. I dont know how to fix it. This is my problem
P.S. I know about [assembly: SecurityRules(SecurityRuleSet.Level1)] , but I'd like to know how to get things working in .net 4.0 security model
Edit: After adding [assembly: AllowPartialTrustCallers] I got a bunk of new problems: I need explicitly mark with [SecuritySafeCritical] all code which creates nlog loggger using LogManager.GetCurrentClassLogger() and then all code that uses the initialized field with logger. It is not acceptable. So maybe there are other ways around?
I think, I just have to move classes used by UntrustedClass and code that use security critical code into separate assembly.
I.e. Move BaseClass, Marshallable, Factory into some Api.dll. Then mark the assembly with [APTCA] and decorate neccessary methods with [SecuritySafe]. Then any existing code should work without problems since [APTCA] is applied only for Api.dll.
This approach works on the sample project, but I cant say for sure if it suits to my current project. As soon as I implement it, i let you know.
I'm trying to find the assembly of a page request during runtime. I have used the code at Get current System.Web.UI.Page from HttpContext? which work for most calls, but there is an issue.
If in my aspx.cs I instantiate a class variable at the top of my class HttpContext.Current.CurrentHandler is null.
Example
I have one DLL named Business.dll with the function to get the Page type as per the above SO question.
In my page, default.asp in FrontEnd.dll I have the following call:
public partial class FrontEnd: Page
{
private readonly Type _t = Business.GetPageType();
The above code return HttpContext.Current.CurrentHandler as null and HttpContext.Current.ApplicationInstance return HttpApplication as the type, and hence System.Web as the assembly.
If I however write it like this:
public partial class FrontEnd: Page
{
readonly Type _t;
protected override void OnInit(EventArgs e)
{
_t = Business.GetPageType();
it works just fine, and I get a reference to CurrentHandler and the page. I could of course refactor all places and move the variable initialization to OnInit, but this requires convention in the app and a higher degree of maintenance.
Using Assembly.GetEntryAssembly() return null for the example and Assembly.GetExecutingAssembly() return Business.dll, so I cannot use them either.
Is there possible another way to find the type/dll, perhaps using the Request Url to find the type/dll which it originates from?
[Update]
So far I have this code, as all my dll's are signed with a known key (not including the extra methods for checking the signing key):
StackTrace stackTrace = new StackTrace();
StackFrame[] stackFrames = stackTrace.GetFrames();
Assembly firstAssembly = null;
foreach (StackFrame stackFrame in stackFrames)
{
var method = stackFrame.GetMethod();
Type t = method.DeclaringType;
if (t != null && t.Assembly.Is(SignedBy.Me))
{
firstAssembly = t.Assembly;
}
}
if( firstPzlAssembly != null)
{
return firstPzlAssembly;
}
While it works, it seems wrong and will have a potential performance hit if called often.
When you do it this way:
private readonly Type _t = Business.GetPageType();
it is actually compiled into a field initialization within the constructor.
It means that the object (your page) is not constructed yet. It does not exist yet, it is "being born". You are just within the constructor at this stage.
Until your object (the page) is constructed, ASP.NET infrastructure cannot assign it to an HttpContext.Current.CurrentHandler static property. Well, because the handler (your page) does not exist yet and id being constructed.
So you cannot do what you want.
What you can do is to create a PageBase class, override OnInit method and add this code there:
public abstract class PageBase
{
protected Type PageType { get; private set; }
protected override void OnInit(EventArgs e)
{
PageType = Business.GetPageType();
}
}
and now just derive your pages from this base class:
public partial class FrontEnd: PageBase { .... }
(or specify PageBase as a base class directly in ASPX file, whatever you do.
One option, is to define a single base page, with the OnInit function setup as required, and then ensure that all your other pages inherit from that.
For example:
public class CustomBasePage: Page
{
readonly Type _t;
protected override void OnInit(EventArgs e)
{
_t = Business.GetPageType();
}
}
Then alter all your pages to inherit from this instead of the normal Page class:
public partial class FrontEnd: CustomBasePage
This means you only need to define your logic once and places a minimal overhead on the rest of the applications pages.
If any page need to override OnInit for another reason, it just needs to include a call to base.OnInit(); which isn't too onerous.
You'll need to ensure that your Business.GetPageType(); return what you expect. I'm not clear on exactly what it is doing and whether it will return the FrontEnd or CustomBasePage class, (nor which of those would be acceptable to your application logic).
I have a generic base wrapper class to wrap a couple of com components we're using:
public class WrapperBase<T> : IDisposable
where T : new()
{
private T comObject = default(T);
private ComponentParameters parameters = null;
protected WrapperBase()
{
comObject = new T();
Initialize();
}
public void SetParameters(ComponentParameters parameters)
{
// ...
this.parameters = parameters;
}
// ...
}
Now I have a concrete wrapper class which inherits from this base class:
public class UserWrapper : WrapperBase<CUserClass>
{
public UserWrapper() : base() { }
public void SomeUserWrapperMethod()
{
// ...
}
}
The type used (CUserClass) is a COM interop type. This type is available after adding the COM object as a reference to the project.
Now I use this class in another assembly (which references the assembly in which the above types are defined):
using (var user = new UserWrapper())
{
user.SomeUserWrapperMethod();
}
The above code compiles fine, but if I'm actually calling the SetParameters method (which is only defined in the Wrapperbase class):
using (var user = new UserWrapper())
{
user.SetParameters(someParameters);
}
I get a (double) compilation error:
error CS0012: The type 'ComponentsAssembly.CUserClass' is defined
in an assembly that is not referenced. You must add a reference to assembly
'Interop.ComponentsAssembly,
Version=1.0.0.0, Culture=neutral,
PublicKeyToken=...'.
ComponentWrappers.dll: (Related file)
error CS0310: 'ComponentsAssembly.CUserClass' must be a
non-abstract type with a public parameterless constructor in order to
use it as parameter 'T' in the generic type or method 'Contact.Wrappers.WrapperBase'
I've tried adding a reference to the component, but I still get the same error message.
This used to work before I changed the WrapperBase to a generic type, but then there was a lot of code identical in all the concrete wrappers, so I refactored it, making the WrapperBase generic in the process.
I can actually solve this by adding a constructor to the concrete class which takes a "ComponentParameters" as a parameter and calls the SetParameters internally:
public class UserWrapper : WrapperBase<CUserClass>
{
public UserWrapper() : base() { }
public UserWrapper(ComponentParameters parameters) : base()
{
SetParameters(parameters)
}
}
And then using it like this:
using (var user = new UserWrapper(someParameters))
{
user.SomeUserWrapperMethod();
}
But I'd rather have both methods working (using a constructor and explicitly calling SetParameters).
Can anybody explain to me what exactly is happening here, as I've been banging my head against the wall for the last couple of hours.
Try this: when you add the COM reference in ComponentWrappers.dll, it generates an interop assembly for it and puts it in the build location (bin/release whatever) for that assembly. In main, navigate to that location and add a a .NET reference (not COM reference) and navigate to that build location and take the interop as the reference.
I think it may be getting confused because you added it as COM in both places and it generated an interop in each...
The other option is to completely prevent the COM artifact from having any public visibility outside your assembly. To do this you'd have to hide it internally in a class, etc.