| File: DiaSymReader\SymUnmanagedFactory.cs | Web Access |
| Project: src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis) |
// 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 file in the project root for more information. #nullable disable using System; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.DiaSymReader { internal static class SymUnmanagedFactory { private const string AlternateLoadPathEnvironmentVariableName = "MICROSOFT_DIASYMREADER_NATIVE_ALT_LOAD_PATH"; private const string LegacyDiaSymReaderModuleName = "diasymreader.dll"; private const string DiaSymReaderModuleName32 = "Microsoft.DiaSymReader.Native.x86.dll"; private const string DiaSymReaderModuleNameAmd64 = "Microsoft.DiaSymReader.Native.amd64.dll"; private const string DiaSymReaderModuleNameArm64 = "Microsoft.DiaSymReader.Native.arm64.dll"; private const string CreateSymReaderFactoryName = "CreateSymReader"; private const string CreateSymWriterFactoryName = "CreateSymWriter"; // CorSymWriter_SxS from corsym.idl private const string SymWriterClsid = "0AE2DEB0-F901-478b-BB9F-881EE8066788"; // CorSymReader_SxS from corsym.idl private const string SymReaderClsid = "0A3976C5-4529-4ef8-B0B0-42EED37082CD"; private static Type s_lazySymReaderComType, s_lazySymWriterComType; internal static string DiaSymReaderModuleName => RuntimeInformation.ProcessArchitecture switch { Architecture.X86 => DiaSymReaderModuleName32, Architecture.X64 => DiaSymReaderModuleNameAmd64, Architecture.Arm64 => DiaSymReaderModuleNameArm64, _ => throw new NotSupportedException() }; [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] [DllImport(DiaSymReaderModuleName32, EntryPoint = CreateSymReaderFactoryName)] private static extern void CreateSymReader32(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object symReader); [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] [DllImport(DiaSymReaderModuleNameAmd64, EntryPoint = CreateSymReaderFactoryName)] private static extern void CreateSymReaderAmd64(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object symReader); [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] [DllImport(DiaSymReaderModuleNameArm64, EntryPoint = CreateSymReaderFactoryName)] private static extern void CreateSymReaderArm64(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object symReader); [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] [DllImport(DiaSymReaderModuleName32, EntryPoint = CreateSymWriterFactoryName)] private static extern void CreateSymWriter32(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object symWriter); [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] [DllImport(DiaSymReaderModuleNameAmd64, EntryPoint = CreateSymWriterFactoryName)] private static extern void CreateSymWriterAmd64(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object symWriter); [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] [DllImport(DiaSymReaderModuleNameArm64, EntryPoint = CreateSymWriterFactoryName)] private static extern void CreateSymWriterArm64(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object symWriter); [DllImport("kernel32")] private static extern IntPtr LoadLibrary(string path); [DllImport("kernel32")] private static extern bool FreeLibrary(IntPtr hModule); [DllImport("kernel32")] private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); private delegate void NativeFactory(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object instance); private static readonly Lazy<Func<string, string>> s_lazyGetEnvironmentVariable = new Lazy<Func<string, string>>(() => { try { foreach (var method in typeof(Environment).GetTypeInfo().GetDeclaredMethods("GetEnvironmentVariable")) { var parameters = method.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string)) { return (Func<string, string>)method.CreateDelegate(typeof(Func<string, string>)); } } } catch { } return null; }); // internal for testing internal static string GetEnvironmentVariable(string name) { try { return s_lazyGetEnvironmentVariable.Value?.Invoke(name); } catch { return null; } } private static object TryLoadFromAlternativePath(Guid clsid, string factoryName) { var dir = GetEnvironmentVariable(AlternateLoadPathEnvironmentVariableName); if (string.IsNullOrEmpty(dir)) { return null; } var moduleHandle = LoadLibrary(Path.Combine(dir, DiaSymReaderModuleName)); if (moduleHandle == IntPtr.Zero) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } object instance = null; try { var createAddress = GetProcAddress(moduleHandle, factoryName); if (createAddress == IntPtr.Zero) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } #if NETSTANDARD1_1 var creator = (NativeFactory)Marshal.GetDelegateForFunctionPointer(createAddress, typeof(NativeFactory)); #else var creator = Marshal.GetDelegateForFunctionPointer<NativeFactory>(createAddress); #endif creator(ref clsid, out instance); } finally { if (instance == null && !FreeLibrary(moduleHandle)) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } } return instance; } private static Type GetComTypeType(ref Type lazyType, Guid clsid) { if (lazyType == null) { #if NET6_0_OR_GREATER System.Diagnostics.Debug.Assert(OperatingSystem.IsWindows()); #endif lazyType = Marshal.GetTypeFromCLSID(clsid); } return lazyType; } internal static object CreateObject(bool createReader, bool useAlternativeLoadPath, bool useComRegistry, out string moduleName, out Exception loadException) { object instance = null; loadException = null; moduleName = null; var clsid = new Guid(createReader ? SymReaderClsid : SymWriterClsid); try { try { switch (RuntimeInformation.ProcessArchitecture, createReader) { case (Architecture.X86, true): CreateSymReader32(ref clsid, out instance); break; case (Architecture.X86, false): CreateSymWriter32(ref clsid, out instance); break; case (Architecture.X64, true): CreateSymReaderAmd64(ref clsid, out instance); break; case (Architecture.X64, false): CreateSymWriterAmd64(ref clsid, out instance); break; case (Architecture.Arm64, true): CreateSymReaderArm64(ref clsid, out instance); break; case (Architecture.Arm64, false): CreateSymWriterArm64(ref clsid, out instance); break; default: throw new NotSupportedException(); } } catch (DllNotFoundException e) when (useAlternativeLoadPath) { instance = TryLoadFromAlternativePath(clsid, createReader ? CreateSymReaderFactoryName : CreateSymWriterFactoryName); if (instance == null) { loadException = e; } } } catch (Exception e) { loadException = e; instance = null; } if (instance != null) { moduleName = DiaSymReaderModuleName; } else if (useComRegistry) { // Try to find a registered CLR implementation try { var comType = createReader ? GetComTypeType(ref s_lazySymReaderComType, clsid) : GetComTypeType(ref s_lazySymWriterComType, clsid); instance = Activator.CreateInstance(comType); moduleName = LegacyDiaSymReaderModuleName; } catch (Exception e) { loadException = e; instance = null; } } return instance; } } }