How to find corresponding .pdb inside .NET assembly? - c#

Apparently, when a .NET assembly is created the location of the corresponding .pdb file path is included inside. Link for reference:
https://msdn.microsoft.com/en-us/library/ms241613.aspx
How do I access this? I have tried using ILSpy to look inside my assembly but could not find.

You can use the dumpbin tool from a Developer Command Prompt, e.g. a cmd line like this
dumpbin /HEADERS YourAssembly.exe
would show the path to the PDB file in the Debug Directories section similar to this
Microsoft (R) COFF/PE Dumper Version 14.00.24213.1
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file YourAssembly.exe
...
Debug Directories
Time Type Size RVA Pointer
-------- ------- -------- -------- --------
570B267F cv 11C 0000264C 84C Format: RSDS, {241A1713-D2EF-4838-8896-BC1C9D118E10}, 1,
C:\temp\VS\obj\Debug\YourAssembly.pdb
...

I have come to the following hacky solution.
It works for me, but I cannot guarantee its correctness :)
public string GetPdbFile(string assemblyPath)
{
string s = File.ReadAllText(assemblyPath);
int pdbIndex = s.IndexOf(".pdb", StringComparison.InvariantCultureIgnoreCase);
if (pdbIndex == -1)
throw new Exception("PDB information was not found.");
int lastTerminatorIndex = s.Substring(0, pdbIndex).LastIndexOf('\0');
return s.Substring(lastTerminatorIndex + 1, pdbIndex - lastTerminatorIndex + 3);
}
public string GetPdbFile(Assembly assembly)
{
return GetPdbFile(assembly.Location);
}

Some time has passed, and now we have funky new .net core tools.
You can now do this easily:
private static void ShowPDBPath(string assemblyFileName)
{
if (!File.Exists(assemblyFileName))
{
Console.WriteLine( "Cannot locate "+assemblyFileName);
}
Stream peStream = File.OpenRead(assemblyFileName);
PEReader reader = new PEReader(peStream);
foreach (DebugDirectoryEntry entry in reader.ReadDebugDirectory())
{
if (entry.Type == DebugDirectoryEntryType.CodeView)
{
CodeViewDebugDirectoryData codeViewData = reader.ReadCodeViewDebugDirectoryData(entry);
Console.WriteLine( codeViewData.Path);
break;
}
}
}

Related

Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host

The application is in Azure Functions,
The error that we are getting from container Pod logs is "Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.26.0".
In our application version all of the dll ver is 3.0.30.0
In the "dll" folder of debug is having the version with 3.0.30.0
And in this version 3.0.30.0, it has the class "Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager"
Not sure, where this "assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.26.0" is coming from.
For me this was happening because Azure Functions Core Tools version mismatched due to upgradation of Visual Studio to latest version.
Removing the Azure Function Tools from the system path C:\Users\user.name\AppData\Local\AzureFunctionsTools and Let Visual Studio automatically install Azure Functions Core Tools fixed the issue.
I had the same issue as below log.
Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.25.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
It was due to my base image for azure functions was old. using the newer base image with below tag(mcr.microsoft.com/azure-functions/dotnet:3.4.2) has fixed my issue.
FROM mcr.microsoft.com/azure-functions/dotnet:3.4.2 AS base
WORKDIR /home/site/wwwroot
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:3.1.416 AS build
WORKDIR /src
This is not direct answer to your question but a tool that would answer it for you. As I had a lot of this kind of errors I have written a helper code to do just that. Its written for the .net framework but with minor changes you can have same thing on core.
//folder where dependencies should be found
var dir = #"C:\Repos\Project\bin";
//dll or exe that you want to inspect
var dll = #"C:\Repos\Project\bin\Project.dll";
var asm = Assembly.ReflectionOnlyLoadFrom(dll);
var stack = new Stack<Data>();
stack.Push(new Data {
ReferencesPath = Array.Empty<Assembly>(),
Assembly = asm
});
List<AssemblyName> visited = new List<AssemblyName>();
while (stack.Any())
{
var current = stack.Pop();
var dependencies = current.Assembly.GetReferencedAssemblies();
visited.Add(current.Assembly.GetName());
foreach (var item in dependencies)
{
if (!visited.Any(x => x.FullName == item.FullName))
{
Assembly dependency;
try
{
dependency = Assembly.ReflectionOnlyLoad(item.FullName);
}
catch
{
var path = Path.Combine(dir, item.Name) + ".dll";
dependency = Assembly.ReflectionOnlyLoadFrom(path);
}
if (dependency.GetName().Version != item.Version)
{
; // put breakpoint here and inspect dependency
// and item when you find your dll in wrong version
// you can inspect current.ReferencesPath to see dependencies
// chain that causes the error
}
stack.Push(new Data
{
Assembly = dependency,
ReferencesPath = current.ReferencesPath.Concat(
new[] { current.Assembly }).ToArray()
});
}
}
}
class Data
{
public Assembly[] ReferencesPath { get; set; }
public Assembly Assembly { get; internal set; }
}

Quantum Program The name 'BellTest' does not exist in the current context

This is my first Q# program and i'm following this getting started link.https://learn.microsoft.com/en-us/quantum/quantum-writeaquantumprogram?view=qsharp-preview
Error is
The name 'BellTest' does not exist in the current context
but its defined in the Bell.cs
I followed the steps and when building its having errors. I'm not sure how to import the operations from .qs file to driver c# file as this error looks like it can't find that operation.
Any help is really appreciated
Here is the code
Driver.cs
using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;
namespace Quantum.Bell
{
class Driver
{
static void Main(string[] args)
{
using (var sim = new QuantumSimulator())
{
// Try initial values
Result[] initials = new Result[] { Result.Zero, Result.One };
foreach (Result initial in initials)
{
var res = BellTest.Run(sim, 1000, initial).Result;
var (numZeros, numOnes) = res;
System.Console.WriteLine(
$"Init:{initial,-4} 0s={numZeros,-4} 1s={numOnes,-4}");
}
}
System.Console.WriteLine("Press any key to continue...");
System.Console.ReadKey();
}
}
}
Bell.qs
namespace Quantum.Bell
{
open Microsoft.Quantum.Primitive;
open Microsoft.Quantum.Canon;
operation Set (desired:Result,q1:Qubit) : ()
{
body
{
let current = M(q1);
if (desired != current)
{
X(q1);
}
}
}
operation BellTest (count : Int, initial: Result) : (Int,Int)
{
body
{
mutable numOnes = 0;
using (qubits = Qubit[1])
{
for (test in 1..count)
{
Set (initial, qubits[0]);
let res = M (qubits[0]);
// Count the number of ones we saw:
if (res == One)
{
set numOnes = numOnes + 1;
}
}
Set(Zero, qubits[0]);
}
// Return number of times we saw a |0> and number of times we saw a |1>
return (count-numOnes, numOnes);
}
}
}
I also got the same error, but I was able to do it by pressing the F5 key.
Perhaps the Visual Studio editor is not yet fully support to the .qs file.
Namespace sharing does not seem to be working properly between .cs file and .qs file.
I was able to execute using your code in my development environment.
--
IDE: Visual Studio Community 2017 (Version 15.5.2)
Dev Kit: Microsoft Quantum Development Kit (0 and 1)
I engage the same problem in microsoft.quantum.development.kit/0.3.1811.203-preview version.
The BellTest operation cannot recognised by VSC Pic of VSCode
What I do is,
save all but keep VSCode open
go to directory and delete anything in bin/ obj/ by /bin/rm -rf bin obj
dotnet run
you go back to check VSCode, the BellTest recognised by VSC now.

LegalCopyRight is always empty in FileVersionInfo C#?

I have a SFX(self-extracting executable) file in windows (Created with zip tools like 7z, WinRar, ....) with the following details:
I want to get CopyRight text in C#, So I wrote the following code:
var fileVersionInfo = FileVersionInfo.GetVersionInfo(filePath);
Console.Write(fileVersionInfo.LegalCopyright)
fileVersionInfo.LegalCopyright is always empty!
What's the problem?
Edit:My original Code:
var fileVersionInfo = FileVersionInfo.GetVersionInfo(filePath1);
var properties = typeof(FileVersionInfo).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var propertyInfo in properties)
{
var value = propertyInfo.GetValue(fileVersionInfo);
Console.WriteLine("{0} = {1}", propertyInfo.Name, value);
}
Console.ReadKey();
The result:
(My reputation is too low to make a comment, so i post it here)
I have just tested the following code, and it works normally for me.
var fileVersionInfo = FileVersionInfo.GetVersionInfo(#"C:\Users\usr\Desktop\Game\steamIntegration\steam_api.dll");
Console.Write(fileVersionInfo.LegalCopyright);
Console.ReadLine();
Maybe your permissions are not sufficient enough for that file. Add a *.manifest to your project change the requestedExecutionLevel to:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Maybe that solves your problem.
The behavior you observe is due to a shortcoming in the implementation of the private function GetVersionInfoForCodePage in line 411 of the Microsoft.NET framework class FileVersionInfo, currently in version 4.6.2 and likely much earlier:
// fileVersion is chosen based on best guess. Other fields can be used if appropriate.
return (fileVersion != string.Empty);
This citation from reference source (comment theirs) means that the function will give up an otherwise correctly guessed codepage-specific version info, if its fileVersion member is empty (that of the block, not that in the header, which adds to the confusion).
This is the case with your exe file:
When we patch the framework to use this instead...
return (productVersion != string.Empty);
...it works as expected (tested in both console and Windows application):
So two options:
Compile the exe so that its FileVersion does not end up empty. Hopefully it is not the fault of your compression tool to not transport this information.
File a bug with Microsoft. What I gleaned from their reference source licence, it does not permit to include derived works in any product.
Finally I could find a solution:
1. First install the following package:
Microsoft.WindowsAPICodePack.Shell
It has a dependency package, Nuget install it automatically Microsoft.WindowsAPICodePack.Core
2. Now we can get file properties as the following code
using System;
using System.Reflection;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main()
{
const string filePath1 = #"C:\Users\Mohammad\Downloads\Test\.....exe";
var shellFile = Microsoft.WindowsAPICodePack.Shell.ShellObject.FromParsingName(filePath1);
foreach (var propertyInfo in typeof(ShellProperties.PropertySystem).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var shellProperty = propertyInfo.GetValue(shellFile.Properties.System, null) as IShellProperty;
if (shellProperty?.ValueAsObject == null) continue;
var shellPropertyValues = shellProperty.ValueAsObject as object[];
if (shellPropertyValues != null && shellPropertyValues.Length > 0)
{
foreach (var shellPropertyValue in shellPropertyValues)
Console.WriteLine("{0} = {1}", propertyInfo.Name, shellPropertyValue);
}
else
Console.WriteLine("{0} = {1}", propertyInfo.Name, shellProperty.ValueAsObject);
}
Console.ReadKey();
}
}
}

c# compiler issue only supports language versions up to C# 5 [duplicate]

After installing VS 2015, running csc.exe from command line causes this message to be displayed to console:
This compiler is provided as part of the Microsoft (R) .NET Framework,
but only supports language versions up to C# 5, which is no longer the
latest version. For compilers that support newer versions of the C#
programming language, see
http://go.microsoft.com/fwlink/?LinkID=533240
The link redirects to Roslyn's repository at GitHub.
So, is the a way to run "compilers that support newer versions" (Roslyn) from command line?
It sounds like your path is inappropriate, basically. If you open the "Developer Command Prompt for VS2015" you should have $ProgramFiles(x86)$\MSBuild\14.0\bin early in your path - and the csc.exe in there is Roslyn.
I suspect you're running the version in c:\Windows\Microsoft.NET\Framework\4.0.30319 or similar - which is the legacy one, basically.
Roslyn from command line('cmd'), Windows 10 scenario example:
( Note: No need Visual Studio installed, but only the .NET core )
Open 'cmd' and create folder "dotnet-csharp-tools":
D:>mkdir "dotnet-csharp-tools"
Navigate to folder "dotnet-csharp-tools":
D:>cd "dotnet-csharp-tools"
In folder "dotnet-csharp-tools" download 'nuget.exe' latest version from:
https://www.nuget.org/downloads
Check name of the last version of 'Microsoft.CodeDom.Providers.DotNetCompilerPlatform' from:
https://www.nuget.org/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform/
For example: 'Microsoft.CodeDom.Providers.DotNetCompilerPlatform -Version 3.6.0'
From 'cmd'(opened folder "dotnet-csharp-tools"), run command:
D:\dotnet-csharp-tools>nuget install Microsoft.CodeDom.Providers.DotNetCompilerPlatform -Version 3.6.0
From 'cmd' navigate to 'D:\dotnet-csharp-tools\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\tools\Roslyn472'(warning : folder name 'Roslyn472' may be different, if is other version)
D:\dotnet-csharp-tools>cd Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\tools\Roslyn472
From 'File explorer' find 'csc.exe'(in the current folder 'Roslyn472').
Make copy of 'csc.exe' with name 'csc-roslyn.exe'(name can be whatever).
For 'Windows 10', open:
'Edit system environment variables' -> 'System variables' ->
'path' -> 'Edit' -> 'New' ->
D:\dotnet-csharp-tools\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\tools\Roslyn472
Close and open again 'cmd'(the command prompt).
This 'cmd' restart needed, because 'system environment variables' are edited.
Check if 'csc-roslyn' is recognized by 'cmd' by run command:
csc-roslyn
Create folder 'D:\csharp-projects'(folder name can be whatever)
and create in 'D:\csharp-projects' C# source files, for example:
Vehicle.cs
class Vehicle
{
private string name = "unknown";
private int producedYear = -1;
public Vehicle(string name, int producedYear)
{
this.Name = name;
this.ProducedYear = producedYear;
}
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public int ProducedYear
{
get { return this.producedYear; }
set { this.producedYear = value; }
}
}
Car.cs
class Car : Vehicle
{
private string maker = "unknown";
public Car(string name, int age, string maker)
: base(name, age)
{
this.Maker = maker;
}
public string Maker
{
get { return this.maker; }
set { this.maker = value; }
}
public override string ToString()
{
return $"{this.Name}, {this.ProducedYear}, {this.Maker}";
}
}
Autoservice.cs
using System;
class Autoservice
{
public static void Main()
{
Car car1 = new Car("Ford Taunus", 1971, "Ford");
Car car2 = new Car("Opel Ascona", 1978, "Opel");
Car car3 = new Car("Saab 900", 1984, "Saab");
Console.WriteLine(car1);
Console.WriteLine(car2);
Console.WriteLine(car3);
}
}
Open 'D:\csharp-projects' from 'cmd'(the command prompt) and run command:
csc-roslyn /target:exe /out:Autoservice.exe Vehicle.cs Car.cs Autoservice.cs
Run from 'cmd':
Autoservice.exe
Result should be:
Ford Taunus, 1971, Ford
Opel Ascona, 1978, Opel
Saab 900, 1984, Saab
I suspect the location of the Roslyn compiler moves around a lot based on the Visual Studio you're running.
I was able to find mine by performing a search like this:
cd "\Program Files (x86)"
dir /s csc*.* | findstr Roslyn
My particular csc.exe was located in:
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Roslyn\csc.exe

Guaranteed way to find the filepath of the ildasm.exe and ilasm.exe files regardless of .NET version/environment?

Is there a way to programmatically get the FileInfo/Path of the ildasm.exe/ilasm.exe executables? I'm attempting to decompile and recompile a dll/exe file appropriately after making some alterations to it (I'm guessing PostSharp does something similar to alter the IL after the compilation).
I found a blog post that pointed to:
var pfDir = Environment.GetFolderPath(Environment.SpecialFolders.ProgramFiles));
var sdkDir = Path.Combine(pfDir, #"Microsoft SDKs\Windows\v6.0A\bin");
...
However, when I ran this code the directory did not exist (mainly because my SDK version is 7.1), so on my local machine the correct path is #"Microsoft SDKs\Windows\v7.1\bin". How do I ensure I can actually find the ildasm.exe?
Similarly, I found another blog post on how to get access to ilasm.exe as:
string windows = Environment.GetFolderPath(Environment.SpecialFolder.System);
string fwork = Path.Combine(windows, #"..\Microsoft.NET\Framework\v2.0.50727");
...
While this works, I noticed that I have Framework and Framework64, and within Framework itself I have all of the versions up to v4.0.30319 (same with Framework64). So, how do I know which one to use? Should it be based on the .NET Framework version I'm targetting?
Summary:
How do I appropriately guarantee to find the correct path to ildasm.exe?
How do I appropriately select the correct ilasm.exe to compile?
One option would be to reference Microsoft.Build.Utilities.Core and use:
var ildasm = Microsoft.Build.Utilities.ToolLocationHelper.GetPathToDotNetFrameworkSdkFile("ildasm.exe", TargetDotNetFrameworkVersion.VersionLatest);
var ilasm = Microsoft.Build.Utilities.ToolLocationHelper.GetPathToDotNetFrameworkFile("ilasm.exe", TargetDotNetFrameworkVersion.VersionLatest);
Right now on my machine this returns:
ildasm = C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools\ildasm.exe
ilasm = C:\Windows\Microsoft.NET\Framework\v4.0.30319\ilasm.exe
I recently needed to do this so I trawled the interwebs for all the possible paths of the Windows SDK and search through those in most recent known order. I also check for whether the OS and process is 64bit and then just use that version by looking in the appropriate Program Files folders. I don't think choosing 64-bit over the 32-bit versions of the tools has any great significance. The x86 version of ILAsm can happily assemble 64-bit preferred assemblies without a hitch, it's all IL and not actually executing any of the code.
ILDasm is part of the Windows SDK where ILAsm is just the .NET Framework SDK so here are some static methods to hunt them down with. The code is baked for .NET 4.0 but you could make some minor tweaks to get it building on .NET 2.0 if you want.
// ILDasm.exe will be somewhere in here
private static string FindPathForWindowsSdk()
{
string[] windowsSdkPaths = new[]
{
#"Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\",
#"Microsoft SDKs\Windows\v8.0A\bin\",
#"Microsoft SDKs\Windows\v8.0\bin\NETFX 4.0 Tools\",
#"Microsoft SDKs\Windows\v8.0\bin\",
#"Microsoft SDKs\Windows\v7.1A\bin\NETFX 4.0 Tools\",
#"Microsoft SDKs\Windows\v7.1A\bin\",
#"Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\",
#"Microsoft SDKs\Windows\v7.0A\bin\",
#"Microsoft SDKs\Windows\v6.1A\bin\",
#"Microsoft SDKs\Windows\v6.0A\bin\",
#"Microsoft SDKs\Windows\v6.0\bin\",
#"Microsoft.NET\FrameworkSDK\bin"
};
foreach (var possiblePath in windowsSdkPaths)
{
string fullPath = string.Empty;
// Check alternate program file paths as well as 64-bit versions.
if (Environment.Is64BitProcess)
{
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), possiblePath, "x64");
if (Directory.Exists(fullPath))
{
return fullPath;
}
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), possiblePath, "x64");
if (Directory.Exists(fullPath))
{
return fullPath;
}
}
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), possiblePath);
if (Directory.Exists(fullPath))
{
return fullPath;
}
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), possiblePath);
if (Directory.Exists(fullPath))
{
return fullPath;
}
}
return null;
}
// ILAsm.exe will be somewhere in here
private static string FindPathForDotNetFramework()
{
string[] frameworkPaths = new[]
{
#"Microsoft.NET\Framework\v4.0.30319",
#"Microsoft.NET\Framework\v2.0.50727"
};
foreach (var possiblePath in frameworkPaths)
{
string fullPath = string.Empty;
if (Environment.Is64BitProcess)
{
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), possiblePath.Replace(#"\Framework\", #"\Framework64\"));
if (Directory.Exists(fullPath))
{
return fullPath;
}
}
fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), possiblePath);
if (Directory.Exists(fullPath))
{
return fullPath;
}
}
return null;
}
You can augment this by passing in the executable you are looking for and change Directory.Exists with File.Exists as well, up to you. You could also take the possible lists and put them in a config file so you can add more without recompiling.

Categories

Resources