I have some exe files which has been created using either .net framework 4.5 or .net core 2.1 or .net core 3.1.
I want to get framework name and version information from this DLL using only c# application.
I have written below piece of code which is beneficial and works great with DLL files but not with exe.
var dllInformation = Assembly.LoadFrom(#"D:\\MyProgram.dll");
Console.WriteLine(dllInformation.FullName);
Console.WriteLine(dllInformation.ImageRuntimeVersion);
Console.WriteLine(((TargetFrameworkAttribute)dllInformation.GetCustomAttributes(typeof(TargetFrameworkAttribute)).First()).FrameworkName);
I have also gone through these links but I didn't found them useful for exe files:
information from exe file
Determine .NET Framework version for dll
Please let me know if any suggestions available.
The following program should display the version of the assembly. The program
loads two assemblies during runtime using Assembly.LoadFrom method. 1) is a .NET Fx assembly and 2) is a .NET Core assembly. It loads both and displays the framework version without issues. This project is in github. If you are using the github project, you need to have .NET Core 3.1 installled.
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Versioning;
namespace net007
{
class Program
{
static void Main(string[] args)
{
//A .net framwwork dll in the same output fodler
//as the current executable
var fxAssembly = Assembly.LoadFrom("fx.console.app.exe");
//A .net core dll in the same output fodler
//as the current executable
var netCoreAssembly = Assembly.LoadFrom("core.console.app.dll");
ShowFrameworkVersion(fxAssembly); //.NETFramework,Version=v4.7.2
ShowFrameworkVersion(netCoreAssembly);//.NETCoreApp,Version = v3.1
}
static void ShowFrameworkVersion(Assembly assembly)
{
var attributes = assembly.CustomAttributes;
foreach (var attribute in attributes)
{
if (attribute.AttributeType == typeof(TargetFrameworkAttribute))
{
var arg = attribute.ConstructorArguments.FirstOrDefault();
if (arg == null)
throw new NullReferenceException("Unable to read framework version");
Console.WriteLine(arg.Value);
}
}
}
}
}
You can use PowerShell to detect for the target framework version:
$path = "C:\your dll here.dll"
[Reflection.Assembly]::ReflectionOnlyLoadFrom($path).CustomAttributes |
Where-Object {$_.AttributeType.Name -eq "TargetFrameworkAttribute" } |
Select-Object -ExpandProperty ConstructorArguments |
Select-Object -ExpandProperty value
This is my full class to get an "Name.exe" target framework.
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Versioning;
public class TargetVersionChecker : MarshalByRefObject
{
public string GetTargetedFrameWork(string exePath)
{
Assembly fxAssembly;
try
{
fxAssembly = Assembly.ReflectionOnlyLoadFrom(exePath);
var targetFrameworkAttribute = fxAssembly.GetCustomAttributesData().FirstOrDefault(x => x.AttributeType == typeof(TargetFrameworkAttribute));
return targetFrameworkAttribute?.ConstructorArguments.FirstOrDefault().Value.ToString();
}
catch (Exception ex)
{
// I log here the error but is to specific to our system so I removed it to be more simple code.
return string.Empty;
}
}
}
Related
Is it possible, from code, to get some project's output path from its vb/csproj alone? I am using .Net framework (4.8). I could not find anything exactly matching what I am looking for. There is https://learn.microsoft.com/en-us/dotnet/api/microsoft.build.evaluation.project.getpropertyvalue?view=msbuild-17-netcore but this isn't available in the .Net framework.
EDIT
I am coding an output builder, which takes targeted assemblies of a solution projects and copy them in a specified location.
The code below uses MSBuild to get the properties of a csproj file specified as a string, and retrieves the current TargetPath. If you create a Console App in .NET Framework 4.8 it will get the output library path of a .NET Framework project.
using Microsoft.Build.Evaluation;
using System;
using System.Collections.Generic;
using System.Linq;
namespace PathGetter
{
internal class Program
{
static void Main(string[] args)
{
string testCsproj = #"C:\Dotnet\WpfControlLibrary1\WpfControlLibrary1\WpfControlLibrary1.csproj";
string result = GetProperty(testCsproj, "TargetPath");
Console.WriteLine(result);
}
public static string GetProperty(string csproj, string propertyName)
{
using (var collection = new ProjectCollection())
{
var project = new Project(csproj, new Dictionary<string, string>(),
null, collection, ProjectLoadSettings.Default);
return project.Properties.Where(p => p.Name == propertyName)
.Select(p => p.EvaluatedValue).SingleOrDefault();
}
}
}
}
I have some code which must be able to generated a console application at runtime (Codegeneration with System.CodeDom). I did this already a lot, but in NET 6 now I am struggling with that and the new API. In the code below I try to compile simply from a string. See below the static class with method Start() which then should generates the application.
The compilations seems fine, no errors at the end. But when starting the generated AppCodegenerated.exe, it shows some reference exception with System.Runtime.
Please help, any Idea? Already researched a lot but could not find any useful solution..
//-
I used the Visual Studio 2022 / NET 6 and theses Nuget's:
using Basic.Reference.Assemblies;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace CompilerSimplified
{
public static class Compiler
{
public static bool Start()
{
string FileName = "AppCodegenerated";
string ExePath = AppDomain.CurrentDomain.BaseDirectory + #"\" + FileName + ".exe";
string code = #"using System; Console.WriteLine(""Hello.""); Console.ReadLine(); ";
// ------- References -------------
// .net platform references
List<MetadataReference> References = new List<MetadataReference>();
foreach (var item in ReferenceAssemblies.Net60) // ReferenceAssemblies from Nuget: Basic.Reference.Assemblies;
References.Add(item);
// or tried this: loop manually through system platform
//string[] fileEntries = Directory.GetFiles(#"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.0\ref\net6.0\", "*.dll");
//foreach (string fileName in fileEntries)
// references.Add(MetadataReference.CreateFromFile(fileName));MetadataReference.CreateFromFile(fileName));
// ------- References END -------------
// delete existing file
if (File.Exists(ExePath))
File.Delete(ExePath);
// compiler options
CSharpCompilationOptions DefaultCompilationOptions =
new CSharpCompilationOptions(outputKind: OutputKind.ConsoleApplication, platform: Platform.AnyCpu)
.WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Release);
// encode soucre code
string sourceCode = SourceText.From(code, Encoding.UTF8).ToString();
// CSharp options
var parsedSyntaxTree = Parse(sourceCode, "", CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10));
// compilation
var compilation = CSharpCompilation.Create(FileName, new SyntaxTree[] { parsedSyntaxTree }, references: References, DefaultCompilationOptions);
var result = compilation.Emit(ExePath);
// return
if (result.Success)
return true;
else
return false;
}
private static SyntaxTree Parse(string text, string filename = "", CSharpParseOptions options = null)
{
var stringText = SourceText.From(text, Encoding.UTF8);
return SyntaxFactory.ParseSyntaxTree(stringText, options, filename);
}
}
}
Above code runs fine without error and exports the AppCodegenerated.exe into the project /bin folder.
Execution of this generated AppCodegenerated.exe shows following on the output console:
Unhandled exception: System.IO.FileNotFoundException:
The file or assembly "System.Runtime, Version = 6.0.0.0, Culture = neutral,
PublicKeyToken = b03f5f7f11d50a3a" or a dependency on it was not found.
The system can not find the stated file.
It is not possible to codegenerate directly a console application like the initial approach above. One possible solution is to generate first a dll (what I mentioned above in the example code is working fine), and from there include that .dll into a .exe, from where the functionality can run.
I cannot understand why I am getting an error (using VS2017) for the code in below related to not finding the class ControlFlowGraph which is supposed to be part of the package Microsoft.CodeAnalysis.FlowAnalysis:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.FlowAnalysis;
namespace CodeAnalysisApp3
{
class Program
{
static async Task Main(string[] args)
{
// Attempt to set the version of MSBuild.
var visualStudioInstances = MSBuildLocator.QueryVisualStudioInstances().ToArray();
var instance = visualStudioInstances[0];
Console.WriteLine($"Using MSBuild at '{instance.MSBuildPath}' to load projects.");
// NOTE: Be sure to register an instance with the MSBuildLocator
// before calling MSBuildWorkspace.Create()
// otherwise, MSBuildWorkspace won't MEF compose.
MSBuildLocator.RegisterInstance(instance);
using (var workspace = MSBuildWorkspace.Create())
{
// Print message for WorkspaceFailed event to help diagnosing project load failures.
workspace.WorkspaceFailed += (o, e) => Console.WriteLine(e.Diagnostic.Message);
var solutionPath = args[0];
Console.WriteLine($"Loading solution '{solutionPath}'");
// Attach progress reporter so we print projects as they are loaded.
var solution = await workspace.OpenSolutionAsync(solutionPath, new ConsoleProgressReporter());
Console.WriteLine($"Finished loading solution '{solutionPath}'");
// TODO: Do analysis on the projects in the loaded solution
CSharpParseOptions options = CSharpParseOptions.Default
.WithFeatures(new[] { new KeyValuePair<string, string>("flow-analysis", "") });
var projIds = solution.ProjectIds;
var project = solution.GetProject(projIds[0]);
Compilation compilation = await project.GetCompilationAsync();
if (compilation != null && !string.IsNullOrEmpty(compilation.AssemblyName))
{
var mySyntaxTree = compilation.SyntaxTrees.First();
// get syntax nodes for methods
var methodNodes = from methodDeclaration in mySyntaxTree.GetRoot().DescendantNodes()
.Where(x => x is MethodDeclarationSyntax)
select methodDeclaration;
foreach (MethodDeclarationSyntax node in methodNodes)
{
var model = compilation.GetSemanticModel(node.SyntaxTree);
node.Identifier.ToString();
if (node.SyntaxTree.Options.Features.Any())
{
var graph = ControlFlowGraph.Create(node, model); // CFG is here
}
}
}
}
}
private class ConsoleProgressReporter : IProgress<ProjectLoadProgress>
{
public void Report(ProjectLoadProgress loadProgress)
{
var projectDisplay = Path.GetFileName(loadProgress.FilePath);
if (loadProgress.TargetFramework != null)
{
projectDisplay += $" ({loadProgress.TargetFramework})";
}
Console.WriteLine($"{loadProgress.Operation,-15} {loadProgress.ElapsedTime,-15:m\\:ss\\.fffffff} {projectDisplay}");
}
}
}
}
However, when I compile the above code I am getting the following error message with VS2017:
1>Program.cs(67,41,67,57): error CS0103: The name 'ControlFlowGraph' does not exist in the current context
1>Done building project "CodeAnalysisApp3.csproj" -- FAILED.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========
Version used:
Microsoft (R) Visual C# Compiler version 4.8.3761.0
for C# 5
Based on my test, I find I can use class ControlFlowGraph.
I installed the following nugetpackage.
Microsoft.CodeAnalysis
Microsoft.Build.Locator
Then, you will see the following result.
Besides, I used .net framwork 4.6.1.
I was able to solve the problem when I used roslyn CodeAnalysis packages with the proper versions:
CodeAnalysis.CSharp.Workspaces (3.4.0)
CodeAnalysis.FlowAnalysis.Utilities (2.9.6)
CodeAnalysis.Workspaces.MSBuild (3.4.0)
The target framework is .NETFramework 4.7.2
A link to a closed issue created for this question on roslyn Github repo is here
ASP.NET Core MVC 2
My web-application will be hosted on IIS 8 (Windows Server 2012 R2). I need to work with ACL of some files and directories. I attached System.IO.FileSystem.AccessControl package to my project, but I have some problems still... Also I see some info about this theme on Microsoft site here. But I don't see the code examples.
I know how to work with ACL through .NET Framework but I don't know how to do it in .NET Core 2. This is my code with my questions (I commented code which works in .NET Framework but doesn't work in .NET Core 2):
using System;
using System.IO;
// NuGet package: System.IO.FileSystem.AccessControl
using System.Security.AccessControl;
using System.Security.Principal;
namespace ACL_Sandbox
{
class Program
{
static void Main(string[] args)
{
try
{
var fileName = "data.txt";
File.Create(fileName);
// How to get the FileSecurity of the file in .NET Core 2?
FileSecurity sec = null; // File.GetAccessControl(true,true,typeof(NTAccount));
AuthorizationRuleCollection rules = sec.GetAccessRules(true, true, null);
foreach (FileSystemAccessRule rule in rules)
{
Console.WriteLine("AccessControlType: {0}", rule.AccessControlType);
Console.WriteLine("FileSystemRights: {0}", rule.FileSystemRights);
Console.WriteLine("IdentityReference.Value: {0}", rule.IdentityReference.Value);
Console.WriteLine("\n=======\n");
}
var sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
string usersAccount = sid.Translate(typeof(NTAccount)).ToString();
FileSystemAccessRule newRule = new FileSystemAccessRule(usersAccount,
FileSystemRights.ExecuteFile, AccessControlType.Allow);
sec.AddAccessRule(newRule);
// How to set the access rule for the file in .NET Core 2?
// File.SetAccessRule(sec);
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex.Message);
}
Console.WriteLine("Press any key for exit...");
Console.ReadKey();
}
}
}
JetBrains Rider IDE for the class FileSecurity shows me such code sources:
// Decompiled with JetBrains decompiler
// Type: System.Security.AccessControl.FileSecurity
// Assembly: System.IO.FileSystem.AccessControl, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// MVID: 95551778-530B-4B9F-8EB6-1D54F85B3C4B
// Assembly location: /usr/share/dotnet/shared/Microsoft.NETCore.App/2.0.5/System.IO.FileSystem.AccessControl.dll
namespace System.Security.AccessControl
{
[SecurityCritical]
public sealed class FileSecurity : FileSystemSecurity
{
public FileSecurity()
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_AccessControl);
}
public FileSecurity(string fileName, AccessControlSections includeSections)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_AccessControl);
}
}
}
This is not encouraging ...
Is it possible to work with ACL in .NET Core 2?
If "yes" then how can I get the the FileSecurity of the file in .NET Core 2?
Also, if "yes" then how can I set the access rule for the file in .NET Core 2?
I haven't tried this with .NET Core 2.2, but with .NET Core 3.0 I have used the following successfully. You must declare using System.Security.AccessControl; and reference the System.IO.FileSystem.AccessControl NuGet package as noted by Jonas in the comments above. The nice thing is that this code is then compatible with .NET Standard 2.0 and .NET Framework 2.0-4.x if you are multi-targeting.
// Setup which parts of the ACL will be modified
AccessControlSections includeSections = AccessControlSections.Access | AccessControlSections.Group | AccessControlSections.Owner;
// FileSecurity sec = File.GetAccessControl(fileName, includeSections);
FileSecurity sec = new FileSecurity(fileName, includeSections);
// ==> Change file security (sec) here
// File.SetAccessControl(fileName, sec);
new FileInfo(fileName).SetAccessControl(sec);
I've been browsing for a good hour and have yet to find something that would help with this. I'm working on opening AutoCAD from the .NET API in VS2013 using C#, but for some reason, I can never get AutoCAD to actually launch. I'm using the following code:
using System;
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
namespace IOAutoCADHandler
{
public static class ACADDocumentManagement
{
[CommandMethod("ConnectToAcad")]
public static void ConnectToAcad()
{
AcadApplication acAppComObj = null;
// no version number so it will run with any version
const string strProgId = "AutoCAD.Application";
// Get a running instance of AutoCAD
try
{
acAppComObj = (AcadApplication)Marshal.GetActiveObject(strProgId);
}
catch // An error occurs if no instance is running
{
try
{
// Create a new instance of AutoCAD
acAppComObj = (AcadApplication)Activator.CreateInstance(Type.GetTypeFromProgID(strProgId), true);
}
catch //// STOPS HERE
{
// If an instance of AutoCAD is not created then message and exit
// NOTE: always shows this box and never opens AutoCAD
System.Windows.Forms.MessageBox.Show("Instance of 'AutoCAD.Application'" +
" could not be created.");
return;
}
}
// Display the application and return the name and version
acAppComObj.Visible = true;
System.Windows.Forms.MessageBox.Show("Now running " + acAppComObj.Name +
" version " + acAppComObj.Version);
// Get the active document
AcadDocument acDocComObj;
acDocComObj = acAppComObj.ActiveDocument;
// Optionally, load your assembly and start your command or if your assembly
// is demandloaded, simply start the command of your in-process assembly.
acDocComObj.SendCommand("(command " + (char)34 + "NETLOAD" + (char)34 + " " +
(char)34 + #"C:\Users\Administrator\Documents\All Code\main-libraries\IOAutoCADHandler\bin\Debug\IOAutoCADHandler.dll" + (char)34 + ") ");
acDocComObj.SendCommand("DRAWCOMPONENT");
}
}
Unfortunately, it always stops at the nested catch statement and always displays the popup box without opening AutoCAD. Any suggestions on how to at least make AutoCAD open for me?
EDIT: Error message
The issue is you're coding (correctly) to the AutoCAD interop interface. I recommend against that (due to potential version changes).
The other issue is that the documentation for AutoCAD plugins using the newer .net api is for plugins when AutoCAD is already running.
Final issue could be that the program Id of AutCAD is a mystery. I have resorted to making that a configurable setting, but default to "AutoCAD.Application", which will take the currently registered AutoCAD.Application on the production machine. If there are multiple versions installed on the machine and you want to be specific, then you could append the version number (which you'll need to research) to the ProgID like: "AutoCAD.Application.19", or "AutoCAD.Application.20" for 2015.
For the first issue, one technique is to use dynamics for the autoCad objects, particularly for creating instances. I have used the ObjectARX api for creating my application in a dummy project, and then switching to dynamics when I'm happy with the properties and method names.
In a standalone .Net application that starts AutoCAD you could use something like:
// I comment these out in production
//using Autodesk.AutoCAD.Interop;
//using Autodesk.AutoCAD.Interop.Common;
//...
//private static AcadApplication _application;
private static dynamic _application;
static string _autocadClassId = "AutoCAD.Application";
private static void GetAutoCAD()
{
_application = Marshal.GetActiveObject(_autocadClassId);
}
private static void StartAutoCad()
{
var t = Type.GetTypeFromProgID(_autocadClassId, true);
// Create a new instance Autocad.
var obj = Activator.CreateInstance(t, true);
// No need for casting with dynamics
_application = obj;
}
public static void EnsureAutoCadIsRunning(string classId)
{
if (!string.IsNullOrEmpty(classId) && classId != _autocadClassId)
_autocadClassId = classId;
Log.Activity("Loading Autocad: {0}", _autocadClassId);
if (_application == null)
{
try
{
GetAutoCAD();
}
catch (COMException ex)
{
try
{
StartAutoCad();
}
catch (Exception e2x)
{
Log.Error(e2x);
ThrowComException(ex);
}
}
catch (Exception ex)
{
ThrowComException(ex);
}
}
}
When there are several versions of AutoCAD installed on a computer, creating an instance with the ProgID "AutoCAD.Application" will run the latest version started on this computer by the current user. If the version of the Interop assemblies used does not match the version that is starting, you'll get a System.InvalidCastException with an HRESULT 0x80004002 (E_NOINTERFACE).
In your specific case, the {070AA05D-DFC1-4E64-8379-432269B48B07} IID in your error message is the GUID for the AcadApplicationinterface in R19 64-bit (AutoCAD 2013 & 2014). So there is an AutoCAD 2013 or 2014 that is starting, and you cannot cast this COM object to a 2015 type because 2015 is R20 (not binary compatible).
To avoid that, you can add a specific version to your ProgID (like "AutoCAD.Application.20" for AutoCAD 2015 (R20.0) to 2016 (R20.1)) to start the version matching your Interop assemblies or you can use late binding (eg. remove your references to Autodesk.AutoCAD.Interop* and use the dynamic keyword instead of the AutoCAD types).
In the last case, you will lost autocompletion, but your program will work with all the versions of AutoCAD.
Check also 32-bit vs 64-bit because TypeLib/Interop assemblies are not the same.
I open the application in a much straight-forward way. First, be sure to reference the correct type library. The one I am using is AutoCAD 2014 Type Library, located at:
c:\program files\common files\autodesk shared\acax19enu.tlb
To initialize the application:
using AutoCAD;
namespace test
{
class Program
{
static void Main(string[] args)
{
AutoCAD.AcadApplication app;
app = new AcadApplication();
app.Visible = true;
Console.Read();
}
}
}
Try this:
"sourcefile" is the original file
"newfile" is the new file
[CommandMethod("ModifyAndSaveas", CommandFlags.Redraw | CommandFlags.Session)]
public void ModifyAndSaveAs()
{
Document acDoc = Application.DocumentManager.Open(sourcefile);
Database acDB = acDoc.Database;
Transaction AcTran = acDoc.Database.TransactionManager.StartTransaction();
using (DocumentLock acLckDoc = acDoc.LockDocument())
{
using (AcTran)
{
BlockTable acBLT = (BlockTable)AcTran.GetObject(acDB.BlockTableId, OpenMode.ForRead);
BlockTableRecord acBLTR = (BlockTableRecord)AcTran.GetObject(acBLT[BlockTableRecord.ModelSpace], OpenMode.ForRead);
var editor = acDoc.Editor;
var SelectionSet = editor.SelectAll().Value;
foreach (ObjectId id in SelectionSet.GetObjectIds())
{
Entity ent = AcTran.GetObject(id, OpenMode.ForRead) as Entity;
//modify entities
}
AcTran.Commit();
}
}
acDB.SaveAs(newfile, DwgVersion.AC1021);
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Tekkit
{
class Program
{
static void Main(string[] args)
{
//make sure to add last 2 using statements
ProcessStartInfo start = new ProcessStartInfo("calc.exe");
Process.Start(start);//starts the process
}
}
}