Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Reader of [Portable PDBs](https://github.com/dotnet/core/blob/main/Documentation/diagnostics/portable_pdb.md) format that implements DiaSymReader interfaces ([ISymUnmanagedReader](https://msdn.microsoft.com/en-us/library/ms232131.aspx), [ISymUnmanagedBinder](https://msdn.microsoft.com/en-us/library/ms232451.aspx), etc.).

The definitions of the DiaSymReader COM interfaces are provided by [Microsoft.DiaSymReader](https://www.nuget.org/packages/Microsoft.DiaSymReader) package.
The implementation of these interfaces for Windows PDBs is provided by [Microsoft.DiaSymReader.Native](https://www.nuget.org/packages/Microsoft.DiaSymReader.Native) package.
The implementation of these interfaces for Windows PDBs is provided by `Microsoft.DiaSymReader.Native.{platform}.dll`, which ships in the .NET runtime.

It is recommended that new applications and libraries read Portable PDBs directly using APIs provided by [System.Reflection.Metadata](https://www.nuget.org/packages/System.Reflection.Metadata) package. These APIs are much more efficient than DiaSymReader APIs. Microsoft.DiaSymReader.PortablePdb bridge is recommended for existings apps that already use DiaSymReader APIs and need to be able to read Portable PDBs without significant changes to their source.

Expand Down
7 changes: 6 additions & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{
"tools": {
"dotnet": "11.0.100-preview.5.26227.104"
"dotnet": "11.0.100-preview.5.26227.104",
"runtimes": {
"dotnet/x86": [
"11.0.0-preview.5.26227.104"
]
}
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26310.1"
Expand Down
6 changes: 6 additions & 0 deletions src/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@
<!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the License.txt file in the project root for more information. -->
<Project>
<Import Project="Sdk.targets" Sdk="Microsoft.DotNet.Arcade.Sdk" />

<PropertyGroup>
<!-- Framework x86 uses xunit.console.x86.exe. Core x86 needs a Microsoft.DiaSymReader
that passes Guid by pointer (not yet in the 2.2.10 package). -->
<TestArchitectures Condition="'$(OS)' == 'Windows_NT' and '$(TargetFrameworkIdentifier)' == '.NETFramework'">x64;x86</TestArchitectures>
</PropertyGroup>
</Project>
1 change: 0 additions & 1 deletion src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
<PackageVersion Include="Microsoft.DiaSymReader" Version="2.2.10" />
<PackageVersion Include="System.Collections.Immutable" Version="10.0.10" />
<PackageVersion Include="System.Reflection.Metadata" Version="10.0.10" />
<PackageVersion Include="Microsoft.DiaSymReader.Native" Version="17.0.0-beta1.21524.1" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,12 @@
<PropertyGroup>
<TargetFrameworks>$(NetCurrent);$(NetFrameworkCurrent)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!--
We would need to download 32bit dotnet cli, which would add extra time to PR runs
Testing 64bit only on Desktop suffixiently covers our interop code paths.
-->
<TestArchitectures Condition="'$(TargetFramework)' == 'net46'">x64;x86</TestArchitectures>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.DiaSymReader.PortablePdb\Microsoft.DiaSymReader.PortablePdb.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.DiaSymReader.Native" GeneratePathProperty="true" />
</ItemGroup>

<ItemGroup>
<Compile Remove="Resources\**\*.cs" />
<Content Include="Resources\**\*.cs" />
Expand Down Expand Up @@ -175,17 +166,4 @@
</EmbeddedResource>
</ItemGroup>

<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<Content Include="$(PkgMicrosoft_DiaSymReader_Native)\runtimes\win\native\Microsoft.DiaSymReader.Native.x86.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
<Link>Microsoft.DiaSymReader.Native.x86.dll</Link>
</Content>
<Content Include="$(PkgMicrosoft_DiaSymReader_Native)\runtimes\win\native\Microsoft.DiaSymReader.Native.amd64.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
<Link>Microsoft.DiaSymReader.Native.amd64.dll</Link>
</Content>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the License.txt file in the project root for more information.

#if NETFRAMEWORK

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Roslyn.Test.Utilities
{
internal static class DiaSymReaderNativeRuntime
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr AddDllDirectory(string newDirectory);

[ModuleInitializer]
internal static void AddInstalledDotNetRuntimeDirectory()
{
string directory = GetInstalledDotNetRuntimeDirectory();
if (string.IsNullOrEmpty(directory))
{
throw new InvalidOperationException("Could not locate an installed .NET runtime containing Microsoft.DiaSymReader.Native.");
}

if (AddDllDirectory(directory) == IntPtr.Zero)
{
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
}

private static string GetInstalledDotNetRuntimeDirectory()
{
string root = Environment.GetEnvironmentVariable("DOTNET_ROOT")
?? Environment.GetEnvironmentVariable("DOTNET_INSTALL_DIR");

if (string.IsNullOrEmpty(root))
{
string hostPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH");
if (!string.IsNullOrEmpty(hostPath))
{
root = Path.GetDirectoryName(hostPath);
}
}

if (string.IsNullOrEmpty(root))
{
string dotnetHost = FindDotNetHost();
if (dotnetHost != null)
{
root = Path.GetDirectoryName(dotnetHost);
}
}

if (string.IsNullOrEmpty(root))
{
root = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet");
}

string dllName = IntPtr.Size == 4
? "Microsoft.DiaSymReader.Native.x86.dll"
: RuntimeInformation.ProcessArchitecture == Architecture.Arm64
? "Microsoft.DiaSymReader.Native.arm64.dll"
: "Microsoft.DiaSymReader.Native.amd64.dll";

foreach (string candidate in GetRuntimeRootCandidates(root))
{
string directory = FindRuntimeDirectory(candidate, dllName);
if (directory != null)
{
return directory;
}
}

return null;
}

private static IEnumerable<string> GetRuntimeRootCandidates(string root)
{
if (IntPtr.Size == 4)
{
string rootX86 = Environment.GetEnvironmentVariable("DOTNET_ROOT(x86)")
?? Environment.GetEnvironmentVariable("DOTNET_ROOT_X86");
if (!string.IsNullOrEmpty(rootX86))
{
yield return rootX86;
}

if (!string.IsNullOrEmpty(root))
{
yield return Path.Combine(root, "x86");
}
}

if (!string.IsNullOrEmpty(root))
{
yield return root;
}
}

private static string FindRuntimeDirectory(string root, string dllName)
{
string shared = Path.Combine(root, "shared", "Microsoft.NETCore.App");
if (!Directory.Exists(shared))
{
return null;
}

return Directory.GetDirectories(shared)
.Select(dir => (dir, version: TryParseVersion(Path.GetFileName(dir))))
.Where(item => item.version != null && File.Exists(Path.Combine(item.dir, dllName)))
.OrderByDescending(item => item.version)
.Select(item => item.dir)
.FirstOrDefault();
}

private static Version TryParseVersion(string name)
{
string core = name.Split('-')[0];
return Version.TryParse(core, out var version) ? version : null;
}

private static string FindDotNetHost()
{
string pathEnv = Environment.GetEnvironmentVariable("PATH");
if (pathEnv == null)
{
return null;
}

foreach (string dir in pathEnv.Split(Path.PathSeparator))
{
if (string.IsNullOrWhiteSpace(dir))
{
continue;
}

string candidate = Path.Combine(dir.Trim(), "dotnet.exe");
if (File.Exists(candidate))
{
return candidate;
}
}

return null;
}
}
}

namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
internal sealed class ModuleInitializerAttribute : Attribute
{
}
}

#endif