Is there an alternative to AppDomain.GetAssemblies on portable project? - c#

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;

Related

Can't load assembly that isn't explicitly used in my project

I need to load these 4 assemblies on the fly:
"Microsoft.CodeAnalysis",
"Microsoft.CodeAnalysis.CSharp",
"Microsoft.CodeAnalysis.Features",
"Microsoft.CodeAnalysis.CSharp.Features"
They all come from nuget packages referenced in a project separate from the startup project.
But when I try loading them like this:
Assembly.Load("Microsoft.CodeAnalysis"),
Assembly.Load("Microsoft.CodeAnalysis.CSharp"),
Assembly.Load("Microsoft.CodeAnalysis.Features"),
Assembly.Load("Microsoft.CodeAnalysis.CSharp.Features")
// this doesn't work either:
// Assembly.Load("Microsoft.CodeAnalysis.CSharp.Features, Version=3.9.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
I get a FileNotFoundException on the Assembly.Load(Microsoft.CodeAnalysis.CSharp.Features) call.
However, loading the dll directly from the packages directory like so:
Assembly.LoadFile(#"D:\project\packages\Microsoft.CodeAnalysis.CSharp.Features.3.9.0-2.20525.2\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.Features.dll")
works perfectly fine.
I've also noticed that the Microsoft.CodeAnalysis.CSharp.Features.dll isn't copied to the startup project bin directory, while the three other dlls are. I suspect that it's because I'm not explicitly using that assembly in my own code, it's just loaded using reflection and then immediately sent to external code.
My complete intended usage/implementation looks like this
public static Document CreateDocument(string assemblyName, IEnumerable<PortableExecutableReference> referensMetadata, string documentName = "Script",
IEnumerable<Assembly> hostedAssemblies = null)
{
// To prevent "The language 'C#' is not supported." exception
var _ = typeof(Microsoft.CodeAnalysis.CSharp.Formatting.CSharpFormattingOptions);
var mefHostRequiredAssemblies = new List<Assembly>
{
Assembly.Load("Microsoft.CodeAnalysis"),
Assembly.Load("Microsoft.CodeAnalysis.CSharp"),
Assembly.Load("Microsoft.CodeAnalysis.Features"),
Assembly.Load("Microsoft.CodeAnalysis.CSharp.Features")
};
if (hostedAssemblies != null)
{
mefHostRequiredAssemblies.AddRange(hostedAssemblies);
}
var partTypes = MefHostServices.DefaultAssemblies.Concat(mefHostRequiredAssemblies)
.Distinct()
.SelectMany(x => x.GetTypes())
.ToArray();
var compositionContext = new ContainerConfiguration()
.WithParts(partTypes)
.CreateContainer();
var workspace = new AdhocWorkspace(MefHostServices.Create(compositionContext));
var project = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(),
assemblyName, assemblyName, LanguageNames.CSharp)
.WithMetadataReferences(referensMetadata);
var documentId = DocumentId.CreateNewId(project.Id);
var documentInfo = DocumentInfo.Create(documentId, documentName,
loader: TextLoader.From(
TextAndVersion.Create(
SourceText.From(string.Empty), VersionStamp.Create())
)
);
return workspace.CurrentSolution.AddProject(project)
.AddDocument(documentInfo)
.GetDocument(documentId);
}
My question is what am I doing wrong? How come I can't load references I've explicitly added to the project?

How to force CSharpCodeProvider to compile for a specific target framework?

I've got a solution which contains c# projects, some netstandard 2.0 and others .net4.7. The startup project is of course net47.
At one point, the project creates code using CodeDom and compiles it with CSharpCodeProvider. The problems is that on some machines, it tries to compile the assembly for .netstandard and it fails. The failure is expected: the generated assembly references EF which in only available for full .net framework.
How can I force CSharpCodeProvider to compile against .net47?
public bool GenerateAssembly(
CodeDomBusinessCode compileUnit
, string fileName
, string assembliesPath
, out IEnumerable<string> errors)
{
var provider = new CSharpCodeProvider();
var parameters = new CompilerParameters
{
GenerateExecutable = false,
OutputAssembly = fileName,
GenerateInMemory = false
};
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Runtime.dll");
parameters.ReferencedAssemblies.Add("System.Core.dll");
parameters.ReferencedAssemblies.Add("System.ComponentModel.Composition.dll");
parameters.ReferencedAssemblies.Add(Path.Combine(assembliesPath, "EntityFramework.dll"));
parameters.ReferencedAssemblies.Add("System.ComponentModel.DataAnnotations.dll");
parameters.ReferencedAssemblies.Add(Path.Combine(assembliesPath, "GlobalE.Server.Contracts.dll"));
var results = provider.CompileAssemblyFromDom(parameters, compileUnit.Code);
if (results.Errors.Count > 0)
{
errors = results.Errors.OfType<CompilerError>().Select(x => x.ToString());
return false;
}
errors = null;
return true;
}
The error:
error CS0012: The type 'System.IDisposable' is defined in an assembly
that is not referenced. You must add a reference to assembly
'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.
UPDATE:
If I change all projects to net47 (so that there is no netstandard project in the solution), the error will disappear, but I want to keep as many projects on netstandard as possible.
based on your error, you should add "netstandard.dll" as references and it may cause by this note that in .net 4.7 the "System.IDisposable" is in "mscorlib.dll" and in .netstatndard is in "netstandard.dll".
Try this
var options = new Dictionary<string, string>
{
{ "CompilerVersion", "v4.7" }
};
var provider = new CSharpCodeProvider(options);

Compile Assembly on the fly

I have this code:
static void Main(string[] args)
{
CompilerParameters cp = new CompilerParameters
{
GenerateInMemory = true,
IncludeDebugInformation = false,
};
cp.ReferencedAssemblies.AddRange(new string[]{
"System.dll",
"System.Data.dll",
"System.Xml.dll",
"Microsoft.mshtml.dll",
"System.Windows.Forms.dll"
});
Assembly _assembly = Assembly.GetExecutingAssembly();
StreamReader _textStreamReader = new StreamReader(_assembly.GetManifestResourceStream("myprog.restext.txt"));
string src = _textStreamReader.ReadToEnd();
byte[] code = Convert.FromBase64String(src);
src = Encoding.UTF8.GetString(code);
CompilerResults cr = CSharpCodeProvider.CreateProvider("CSharp").
CompileAssemblyFromSource(cp, src);
Assembly asm = cr.CompiledAssembly;
Type typ = asm.GetType("clicker.Program");
MethodInfo method = typ.GetMethod("DoStart");
method.Invoke(null, new[] { (object)args });
}
I thows FileNotFoundException becouse CompileAssemblyFromSource returns the same error. Source using mshtml.
Then I'm trying to compile it using csc.exe, it says:
error CS0006. (no Metadata for "Microsoft.mshtml.dll")
I think it because mshtml is ActiveX library. So The question is how to assemble source usings activeX mshtml.
p.s.
Source has no errors and successfully has compiled from VS but can't be compiled by "on the fly" compilation.
I thows FileNotFoundException
That's normal, Microsoft.mshtml.dll is a primary interop assembly. It is not part of the .NET Framework so cannot be located automatically. It also won't be available on the user's machine, PIAs have to be installed.
The best way to go about it is to ensure that the assembly is present in your build directory so it will be deployed along with your program and can always be found. Project + Add Reference, select Microsoft.mshtml. Select it from the References node and set the Isolated property to False, Copy Local to True. Rebuild and verify that you now have Microsoft.mshtml.dll present in your bin\Debug directory.
And modify your code to pass the full path name to the file. Like this:
var referenceAssemblies = new List<string> {
"System.dll",
"System.Data.dll",
"System.Xml.dll",
"System.Windows.Forms.dll"
};
var homedir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var mshtml = Path.Combine(homedir, "Microsoft.mshtml.dll");
referenceAssemblies.Add(mshtml);
cp.ReferencedAssemblies.AddRange(referenceAssemblies.ToArray());

Unable to cast object of type XXXXXX to type IXXXXX (.NET 4.0)

Can someone please explain what is happening to me? I have a test project that tests a dummy instance of my service. In the test project, I simply reference the dummyService.exe and System.SystemProcess dll.
In my dummyService project however, I have referenced the class library, which itself uses other dlls from other componentsn as well as other projects in my solution.
The problem is that when I run my test, exceptions get thrown( First Chance exceptions for dlls which are loaded and working in the dummyService), in addition invalidcast exception (error message below).
Unable to cast object of type 'Export.CaseOutputGenerator' to type 'Export.ICaseOutputGenerator'.
System.InvalidCastException was caught
Message=Unable to cast object of type 'Export.CaseOutputProcess.CustomCaseOutputGenerator' to type
'Export.CaseOutputProcess.ICaseOutputGenerator'.
Source=Export.CaseOutputProcess
StackTrace:
at Export.CaseOutputProcess.CaseOutputGeneratoryFactory.GetCaseOutputGeneratorObject(String assemblyName, String className)
in C:\Monitor\Export.CaseOutputProcess\CaseOutputGeneratoryFactory.cs:line 56
at Monitor.BOMock.GenerateCaseOutput(String OutputFolder, String iFile, Int32 seqNum, DataTable CaseSettings, String SettingFileName)
in C:\Monitor\BOMock\BOMock.cs:line 1069
at Monitor.BOMock.Handling() in C:\Monitor\BOMock\BOMock.cs:line 492 InnerException:
public static ICaseOutputGenerator GetCaseOutputGeneratorObject(string assemblyName, string className)
{
ICaseOutputGenerator customeOutputGen = null;
var obj = GetObject(assemblyName, className);
if (obj != null)
caseOutputGen = (ICaseOutputGenerator)obj; // FAILS HERE
return caseOutputGen;
}
private static object GetObject(string fullName, string className)
{
try
{
Type caseOutputGen = null;
var localAssembly = Assembly.LoadFrom(fullName);
foreach (var testType in localAssembly.GetTypes())
{
if (!testType.FullName.EndsWith(className, StringComparison.InvariantCultureIgnoreCase)) continue;
caseOutputGen = testType;
break;
}
if (caseOutputGen == null) return null;
var obj = Activator.CreateInstance(caseOutputGen);
return obj;
}
catch (FileNotFoundException ex)
{
throw new Exception("Failed to load assembly: " + Environment.NewLine + fullName, ex);
}
catch (Exception ex)
{
throw new Exception("Failed to load assembly: " + Environment.NewLine + fullName, ex);
}
}
Where assemblyName is the Path to the dll file to load and className happens to be the name of the class to create an instance of.
In the code, as you see, using reflection I load the assembly at the assemblyName PATH provided (String assemblyName) http://msdn.microsoft.com/en-us/library/system.reflection.assembly.loadfrom.aspx , and then using reflection again, I then create an instance of the className (String className ) contained in the loaded assembly. http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx
How do I remedy this problem please? I don't want to have to reference all my dlls in the test project. How do I get around or solve this problem please?? Thanks in advance.
Based on that stack trace, it looks like the assembly where the type lives is not being found. If you just add the reference to the compiled exe, you're probably not going to get the other libraries along with it. I think you've got a couple of choices:
Go ahead and bite the bullet: add the references to the other libraries in your test project. They're typically not transitive: just because your service knows about them doesn't necessarily follow that your test's assembly knows about them as well.
Add a post-compilation step to your test's project that copies over the other assemblies so that they can be found by the app domain running your test.
Use dependency injection and an inversion of control container. There are quite a few out there, but Castle Windsor, StructureMap and Unity come to mind. Scott Hanselman's got a great list of them on his blog: http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx

How to load an assembly from GAC with wildcards in version number

In our application, we have the need to dynamically load 3rd-party assemblies where we do not know in advance all released assembly version numbers. All we know is, for example, that the major version number for the assembly must be "12". On a PC, multiple versions of the same assembly may be installed, having both higher and lower major version numbers.
I.e. we would need something like
Assembly myAssembly = Assembly.Load("SampleAssembly, Version=12.*.*.*");
and if the assembly versions 11.1.2.3, 12.7.6.5, and 13.9.8.7 are installed, it should load version 12.7.6.5.
I.e. it should be possible to specify wildcards for version number components and it also should be possible to omit Culture and PublicKeyToken.
When we do this with Assembly.Load(), we get a FileNotFoundException.
We cannot use Assembly.LoadWithPartialName() because it always loads the assembly with the highest version number, but we want a specific major version number instead, which possibly is less than the greatest installed assembly version number.
Is it possible to do this?
You could manually list the content of the GAC and compare it to your wildcards as so
class Program
{
static void Main(string[] args)
{
var assemblyName = "SimpleAssembly";
var versionRegex = new Regex(#"^12\.");
var assemblyFile = FindAssemblyFile(assemblyName, versionRegex);
if (assemblyFile == null)
throw new FileNotFoundException();
Assembly.LoadFile(assemblyFile.FullName);
}
static FileInfo FindAssemblyFile(string assemblyName, Regex versionRegex)
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "assembly", "GAC_MSIL", assemblyName);
var assemblyDirectory = new DirectoryInfo(path);
foreach (var versionDirectory in assemblyDirectory.GetDirectories())
{
if (versionRegex.IsMatch(versionDirectory.Name))
{
return versionDirectory.GetFiles()[0];
}
}
return null;
}
}

Categories