I'm trying to create an assembly dynamically in .Net. I can't seem to figure out how to get the CodeBase property to return a value, however. Here's an example:
var assemblyName = new AssemblyName
{
Name = "Whatever",
CodeBase = Directory.GetCurrentDirectory()
};
var assemblyBuilder = AppDomain.CurrentDomain
.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("WhateverModule", "Whatever.dll");
var typeBuilder = moduleBuilder.DefineType("WhateverType", TypeAttributes.Public);
var type = typeBuilder.CreateType();
assemblyBuilder.Save("Whatever.dll");
var codeBase = type.Assembly.CodeBase; // throws the below exception
System.NotSupportedException was unhandled
Message=The invoked member is not supported in a dynamic assembly.
Source=mscorlib
StackTrace:
at System.Reflection.Emit.InternalAssemblyBuilder.get_CodeBase()
at Stupid.Program.Main(String[] args) in C:\Users\Walking Disaster\Documents\Visual Studio 10\Projects\Lingual.Proxy\Stupid\Program.cs:line 25
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Can anyone see what I'm doing wrong?
Explanation: I'm trying to extend NUnit with an addin that generates test methods at runtime. It does this by taking an array of Action delegates. Unfortunately, Reflection is baked pretty deep into the framework, so I have had to emit classes with a method for each Action delegate. This works fine when I'm using the NUnitTestMethod classes only, but when I use the NUnitTestFixture, it fails because it tries to read the CodeBase from the assembly. I didn't want to create a physical assembly, but this project has been one compromise after another.
I think the exception cause is that CodeBase is meaningless on dynamic assembly. From MSDN:
The location of the assembly as specified originally
If you'll reload the assembly it won't throw the exception:
var assemblyName = new AssemblyName
{
Name = "Whatever",
CodeBase = Directory.GetCurrentDirectory()
};
var assemblyBuilder = AppDomain.CurrentDomain
.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(
"WhateverModule", "Whatever.dll");
var typeBuilder =
moduleBuilder.DefineType("WhateverType", TypeAttributes.Public);
var type = typeBuilder.CreateType();
assemblyBuilder.Save("Whatever.dll");
var assembly = Assembly.LoadFrom("Whatever.dll");
var codeBase = assembly.CodeBase; // this won't throw exception
Is the Assembly's Location valid? Could you fork NUnit and use that instead given your reference to being damaged :P
I've spent some hours being subjected to NUnit's 'extensibility' story and can't imagine ever choosing it over xUnit.net - but I guess the decider of that will be how many tests you have...
Related
I trying to get the assemblies list. But I facing an exception in portable UWP project.
The following code works in
.netframework
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
Xamarin portable
var currentdomain = typeof(string).GetTypeInfo().Assembly.GetType("System.AppDomain").GetRuntimeProperty("CurrentDomain").GetMethod.Invoke(null, new object[] { });
var getassemblies = currentdomain.GetType().GetRuntimeMethod("GetAssemblies", new Type[] { });
var assemblies = getassemblies.Invoke(currentdomain, new object[] { }) as Assembly[];
But the same above code is not working in the UWP portable. (I think the portable is working in the UWP too)
I am getting the following issue while hitting the first line
'typeof(string).GetTypeInfo().Assembly.GetType("System.AppDomain").GetRuntimeProperty("CurrentDomain").GetMethod.Invoke(null, new object[] { })' threw an exception of type 'System.InvalidOperationException'
Data: {System.Collections.ListDictionaryInternal}
HResult: -2146233079
HelpLink: null
InnerException: null
Message: "The API 'System.AppDomain.get_CurrentDomain()' cannot be used on the current platform. See http://go.microsoft.com/fwlink/?LinkId=248273 for more information."
Source: "mscorlib"
StackTrace: " at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)\r\n at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)"
If I use the below code
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
Assembly[] assembly = ((dynamic)Thread.GetDomain()).GetAssemblies() as Assembly[];
var loadedAssemblies = ((dynamic)Thread.GetDomain()).GetAssemblies() as Assembly[];
then I get following error.
The name 'AppDomain' does not exist in the current context
The name 'Thread' does not exist in the current context
The name 'Thread' does not exist in the current context
I checked the Is there an alternative to AppDomain.GetAssemblies on portable library?, But this is not helped to fix my issue.
Firstly, .NET Standard libraries are the replacement for Portable Class Libraries (PCL). See the .NET Standard 2.0 Support in Xamarin.Forms. You can use the following code to get all the assemblies in the package of the UWP app,
private async Task<List<Assembly>> GetAssemblyListAsync()
{
var PackageFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;
List<Assembly> assemblies = new List<Assembly>();
foreach (StorageFile file in await PackageFolder.GetFilesAsync())
{
if (file.FileType == ".dll" || file.FileType == ".exe")
{
AssemblyName name = new AssemblyName() { Name = file.Name };
Assembly asm = Assembly.Load(name);
assemblies.Add(asm);
}
}
return assemblies;
}
However, from .Net standard 2.0, you can use AppDomain.GetAssemblies directly in UWP app to get the assemblies.
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
You need to download Visual Studio 2017 15.4 or above and set your app target version and min version Windows 10 Fall Creators Update (16299). Besides, you can also create a .Net Standard 2.0 class library to use the AppDomain.GetAssemblies.
Windows.UI.Xaml.Application.Current.GetType().GetTypeInfo().Assembly;
I am pretty new to C# and .net. and I have been tasked with creating a WPF application POC to read a QTP object repository in a .tsr file and populate a TreeView with all the objects in that repository and their child objects.
I was provided with QTP's RepositoryUtil.dll and the following sample code to adapt to C#:
Dim i
Dim ORFile = "D:\TAF\Size.tsr"
Dim ORObj = CreateObject("Mercury.ObjectRepositoryUtil")
ORObj.Load(ORFile)
Dim ObjectCollection = ORObj.GetChildren
For i = 0 To ObjectCollection.Count - 1
Dim Obj = ObjectCollection.Item(i)
Dim temp1 = "" & Obj.GetTOProperty("micclass") & "(""" & ORObj.GetLogicalName(Obj) & """)"
Dim treeItem = New TreeViewItem
Dim tNode = New TreeViewItem() With {.Header = temp1}
treeview1.Items.Add(tNode)
tNode = treeview1.Items(i)
Add(ORObj, Obj, tNode)
Next
Now I am not very familiar with Visual Basic however I am trying to adapt it as follows in a Console application to check whether the string "title" gets populated.
static void Main(string[] args)
{
string ORFilePath = #"D:\TAF\Size.tsr";
ObjectRepositoryUtil ORUtil = new ObjectRepositoryUtil();
ORUtil.Load(ORFilePath);
var ChildObjects = ORUtil.GetChildren();
for (int i = 0; i < ChildObjects.Count(); i++ )
{
var ChildObject = ChildObjects.Item(i);
string title = ChildObject.GetTOProperty("micclass") + "(\"" + ORUtil.GetLogicalName(ChildObject) + "\")";
Console.WriteLine(title);
}
}
But when I run this, I get an InvalidCastException: {"Return argument has an invalid type."} on the line "var ChildObjects = ORUtil.GetChildren();"
I cannot figure out what is wrong or what type is being returned by ORUtil.GetChildren() since there is no documentation provided with the library and I can't find any online.
So, can anyone tell what exactly am I doing wrong here and what is the proper way to go about this?
Edit:
Here's the stack trace for the above exception:
System.InvalidCastException was unhandled
HResult=-2147467262
Message=Return argument has an invalid type.
Source=mscorlib
StackTrace:
at System.Runtime.Remoting.Proxies.RealProxy.ValidateReturnArg(Object arg, Type paramType)
at System.Runtime.Remoting.Proxies.RealProxy.PropagateOutParameters(IMessage msg, Object[] outArgs, Object returnValue)
at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData)
at REPOSITORYUTILLib.DispIObjectRepositoryUtil.GetChildren(Object Parent)
at ORReader.Program.Main(String[] args) in c:\Users\DDDAVID.DDDAVID-IN\Documents\Visual Studio 2013\Projects\ORReader\ORReader\Program.cs:line 17
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
With some help, I've solved my own problem.
What I was trying to do was trying to add the RepositoryUtil.dll as a reference and calling its methods. What I should have been doing was to get the Program Identifier for QTP's registered ObjectRepositoryUtil using the following:
Type.GetTypeFromProgID("Mercury.ObjectRepositoryUtil");
dynamic ORUtil = Activator.CreateInstance(ORType);
I've modified the code as follows and it worked.
static void Main(string[] args)
{
Type ORType = Type.GetTypeFromProgID("Mercury.ObjectRepositoryUtil");
dynamic ORUtil = Activator.CreateInstance(ORType);
string ORFilePath = #"D:\TAF\Size.tsr";
//ObjectRepositoryUtil ORUtil = new ObjectRepositoryUtil();
ORUtil.Load(ORFilePath);
var ChildObjects = ORUtil.GetChildren();
for (int i = 0; i < ChildObjects.Count(); i++ )
{
var ChildObject = ChildObjects.Item(i);
string title = ChildObject.GetTOProperty("micclass") + "(\"" + ORUtil.GetLogicalName(ChildObject) + "\")";
Console.WriteLine(title);
}
}
I do web. service that load assembly file by input parameter.
Then in assembly will try to find a specific type (inherited from specific interface), create an instance and returns the result of the method.
I need to make the call to the assembly was again released.
From the input parameters of the method I find the path to the the assembly in web.config. and try to load it.
This is the code that works:
[WebMethod]
public String[] GetData(String confKey)
{
var assemblyPath = ConfigurationManager.AppSettings[confKey];
var assembly = Assembly.LoadFrom(assemblyPath);
List<String> retVals = new List<String>();
foreach (var t in assembly.GetTypes())
{
if (t.ImplementsInterface(typeof(IMyServiceProvider)))
{
IMyServiceProvider objectInstance = Activator.CreateInstance(t) as IMyServiceProvider;
retVals.Add(objectInstance.GetData());
}
}
return retVals.ToArray();
}
But this way I can delete the loaded assembly or replace it because the file is "locked".
So I tried to go a different way and load the assembly into own AppDomain like this:
[WebMethod]
public String[] GetData(String confKey)
{
var assemblyPath = ConfigurationManager.AppSettings[confKey];
var tmp = String.Concat("AppDomain", Guid.NewGuid().ToString("N"));
AppDomain dom = AppDomain.CreateDomain(tmp, null, AppDomain.CurrentDomain.SetupInformation);
AssemblyName assemblyName = new AssemblyName();
assemblyName.CodeBase = assemblyPath;
Assembly assembly = dom.Load(assemblyPath);
List<String> retVals = new List<String>();
foreach (var t in assembly.GetTypes())
{
if (t.ImplementsInterface(typeof(IMyServiceProvider)))
{
IMyServiceProvider objectInstance = Activator.CreateInstance(t) as IMyServiceProvider;
retVals.Add(objectInstance.GetData());
}
}
AppDomain.Unload(dom);
return retVals.ToArray();
}
But this solution is thrown Exception:
Could not load file or assembly 'NameOfMyAssembly' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)- at System.Reflection.AssemblyName.nInit(RuntimeAssembly& assembly, Boolean forIntrospection, Boolean raiseResolveEvent)
at System.Reflection.RuntimeAssembly.CreateAssemblyName(String assemblyString, Boolean forIntrospection, RuntimeAssembly& assemblyFromResolveEvent)
at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection)
at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.AppDomain.Load(String assemblyString)
at System.AppDomain.Load(String assemblyString)
Why the first solution assembly loads without problem, and the second throws an error ?
Thanks
dom.Load(string) method tries to load the assembly by its full name which consists of assembly name, version, culture info and public key. CLR search the assemby in GAC and application's directory. Also you could set additional searching paths with .config file (app.config or ) and probing element. In your case as I understand assemblyPath is a path to the assembly you whant to load and it isn't what need to pass as parameter. I used next code to load assemblies in created domain:
var domain = AppDomain.CreateDomain("Plugin domain"); // Domain for plugins
domain.Load(typeof(IPlugin).Assembly.FullName); // Load assembly containing plugin interface to domain
// ...
var asm = Assembly.LoadFrom(pluginFile);
foreach (var exportedType in asm.GetExportedTypes())
{
if (!typeof (IPlugin).IsAssignableFrom(exportedType))
continue; // Check if exportedType implement IPlugin interface
domain.Load(asm.FullName); // If so load this dll into domain
// ...
}
As all plugins lies in "\Plugins" directory relatively application directory I also added .config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Plugins"/>
</assemblyBinding>
</runtime>
</configuration>
Pay attantion that if you whant to use dom.Load(AssemblyName) with specified only CodeBase property you must set it in Uri format (i.e. "file:///D:/Dir/...") and I think these assemblies have to be strongly named if they are not in the current application’s folder.
Also as I see you are usig Activator.CreateInstance() to create objects. In this case although you load assembly in created domain you create objects in current domain and all code of this objects be executed in current domain too. To create and execute code in a separate domain you should use appDomain.CreateInstanceAndUnwrap() method (link)
I am trying to run a simple application that uses drool.net. Whenever I execute, the application crashed at builder.AddPackageFromDrl("DroolsApp.Rules.drl", stream); The Error stated that NullReferenceException was unhandled and Object reference not set an instance of an object. I tried to change drl file but the error was still present. Am I missing anything? Also is there any site that provides a detailed tutorial for using drools.net and do I need any specific program to create and edit drl files as I am using a simple notepad.
Thanks in Advance
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using org.drools.dotnet.compiler;
using org.drools.dotnet.rule;
using org.drools.dotnet;
using System.IO;
using System.Reflection;
namespace DroolsApp
{
class Program
{
static void Main(string[] args)
{
PackageBuilder builder = new PackageBuilder();
Stream stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream("DroolsApp.Rules.drl");
builder.AddPackageFromDrl("DroolsApp.Rules.drl", stream);
Package pkg = builder.GetPackage();
RuleBase ruleBase = RuleBaseFactory.NewRuleBase();
ruleBase.AddPackage(pkg);
executeRules(ruleBase, 5);
executeRules(ruleBase, 6);
executeRules(ruleBase, 7);
}
public static void executeRules(RuleBase ruleBase, int amount)
{
WorkingMemory workingMemory = ruleBase.NewWorkingMemory();
Quest quest = new Quest();
quest.Value = amount;
workingMemory.assertObject(quest);
workingMemory.fireAllRules();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DroolsApp
{
class Quest
{
public int Value { set; get; }
}
}
DRL File:
package DroolsApp
rule "NumberFive"
when
quest : Quest( Value == 5 )
then
Console.WriteLine("FIVE");
end
rule "NumberSix"
when
quest : Quest( Value == 6 )
then
Console.WriteLine("SIX");
end
Stack Trace:
System.NullReferenceException was unhandled
Message=Object reference not set to an instance of an object.
Source=drools.dotnet
StackTrace:
at org.drools.dotnet.semantics.DotnetClassTypeResolver.resolveType(String className)
at org.drools.semantics.java.RuleBuilder.build(ColumnDescr columnDescr)
at org.drools.semantics.java.RuleBuilder.build(RuleDescr ruleDescr)
at org.drools.semantics.java.RuleBuilder.build(Package pkg, RuleDescr ruleDescr)
at org.drools.compiler.PackageBuilder.addRule(RuleDescr ruleDescr)
at org.drools.compiler.PackageBuilder.addPackage(PackageDescr packageDescr)
at org.drools.compiler.PackageBuilder.addPackageFromDrl(String fileName, Reader reader)
at org.drools.dotnet.compiler.PackageBuilder.AddPackageFromDrl(String fileName, Stream drlStream)
at MinimalDroolsForm.Program.Main(String[] args) in C:\Users\Carl\Desktop\DroolsApp\Program.cs:line 19
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
It's for sure an error in drl file. Try to remove all rules and imports, compile them, and then add single line and repeat (it must validate though). Drools.net reacts for compilation errors with NullReferenceException or an error with loading CompiledRules0.dll (because it wasn't generated). It is also a good thing to add a Pre-build Build Event deleting the previously generated CompiledRules0.dll (when Drools.net fails to generate the file it uses the previous one). Here's the code:
cd $(TargetDir)
del CompiledRules0.*
Two hints:
1) when calling GetManifestResourceStream method, be sure to pass namespace and filename as parameter.
2) In visual studio (if you are using it), rule file properites, set build action to embedded resource.
I created a standard XsltContext class and call it as follows:
XPathCustomContext context = new XPathCustomContext(new NameTable());
context.AddNamespace("windward", XPathCustomContext.Namespace);
XsltArgumentList varList = new XsltArgumentList();
varList.AddParam("stat-index", "", 0);
context.ArgList = varList;
XmlDocument doc = new XmlDocument();
doc.Load("c:\\test\\order.xml");
object xx = doc.CreateNavigator().Evaluate("/order[1]/customer[1]/num[#negone = $stat-index]", context);
When running under .net 4.0 it works fine. But under .NET 3.5 (which we have to use at present) I get:
System.Xml.XPath.XPathException was unhandled
Message=XsltContext is needed for this query because of an unknown function.
Source=System.Xml
StackTrace:
at MS.Internal.Xml.XPath.CompiledXpathExpr.UndefinedXsltContext.ResolveVariable(String prefix, String name)
at MS.Internal.Xml.XPath.VariableQuery.SetXsltContext(XsltContext context)
at MS.Internal.Xml.XPath.LogicalExpr.SetXsltContext(XsltContext context)
at MS.Internal.Xml.XPath.FilterQuery.SetXsltContext(XsltContext input)
at MS.Internal.Xml.XPath.CompiledXpathExpr.SetContext(XmlNamespaceManager nsManager)
at System.Xml.XPath.XPathExpression.Compile(String xpath, IXmlNamespaceResolver nsResolver)
at System.Xml.XPath.XPathNavigator.Evaluate(String xpath, IXmlNamespaceResolver resolver)
at CustomXpathFunctions.Program.Main(String[] args) in c:\src\CustomXpathFunctions\Program.cs:line 62
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
Any idea why?
Sample code at http://www.windwardreports.com/temp/CustomXPathFunctionsBug.zip
thanks - dave
Figured out a way to do this. Use an XPathExpression like:
XPathNavigator nav = doc.CreateNavigator();
XPathExpression exp = nav.Compile("/order[1]/customer[1]/num[#negone = $stat]");
exp.SetContext(ctx);
object zzz = nav.Evaluate(exp);
As to why - my guess is Evaluate(string, context) uses it for namespaces only in .NET 3.5 while XPathExpression uses it for everything. The critical point though is this works.
ps - Please upvote the snot out of this answer - this was really hard to figure out.