// dnlib: See LICENSE.txt for more info using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using dnlib.DotNet.MD; namespace dnlib.DotNet { /// /// A high-level representation of a row in the ImplMap table /// [DebuggerDisplay("{Module} {Name}")] public abstract class ImplMap : IMDTokenProvider { /// /// The row id in its table /// protected uint rid; /// public MDToken MDToken => new MDToken(Table.ImplMap, rid); /// public uint Rid { get => rid; set => rid = value; } /// /// From column ImplMap.MappingFlags /// public PInvokeAttributes Attributes { get => (PInvokeAttributes)attributes; set => attributes = (int)value; } /// Attributes protected int attributes; /// /// From column ImplMap.ImportName /// public UTF8String Name { get => name; set => name = value; } /// Name protected UTF8String name; /// /// From column ImplMap.ImportScope /// public ModuleRef Module { get => module; set => module = value; } /// protected ModuleRef module; /// /// Modify property: = /// ( & ) | . /// /// Value to AND /// Value to OR void ModifyAttributes(PInvokeAttributes andMask, PInvokeAttributes orMask) => attributes = (attributes & (int)andMask) | (int)orMask; /// /// Set or clear flags in /// /// true if flags should be set, false if flags should /// be cleared /// Flags to set or clear void ModifyAttributes(bool set, PInvokeAttributes flags) { if (set) attributes |= (int)flags; else attributes &= ~(int)flags; } /// /// Gets/sets the bit /// public bool IsNoMangle { get => ((PInvokeAttributes)attributes & PInvokeAttributes.NoMangle) != 0; set => ModifyAttributes(value, PInvokeAttributes.NoMangle); } /// /// Gets/sets the char set /// public PInvokeAttributes CharSet { get => (PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask; set => ModifyAttributes(~PInvokeAttributes.CharSetMask, value & PInvokeAttributes.CharSetMask); } /// /// true if is set /// public bool IsCharSetNotSpec => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetNotSpec; /// /// true if is set /// public bool IsCharSetAnsi => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetAnsi; /// /// true if is set /// public bool IsCharSetUnicode => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetUnicode; /// /// true if is set /// public bool IsCharSetAuto => ((PInvokeAttributes)attributes & PInvokeAttributes.CharSetMask) == PInvokeAttributes.CharSetAuto; /// /// Gets/sets best fit /// public PInvokeAttributes BestFit { get => (PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask; set => ModifyAttributes(~PInvokeAttributes.BestFitMask, value & PInvokeAttributes.BestFitMask); } /// /// true if is set /// public bool IsBestFitUseAssem => ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitUseAssem; /// /// true if is set /// public bool IsBestFitEnabled => ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitEnabled; /// /// true if is set /// public bool IsBestFitDisabled => ((PInvokeAttributes)attributes & PInvokeAttributes.BestFitMask) == PInvokeAttributes.BestFitDisabled; /// /// Gets/sets throw on unmappable char /// public PInvokeAttributes ThrowOnUnmappableChar { get => (PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask; set => ModifyAttributes(~PInvokeAttributes.ThrowOnUnmappableCharMask, value & PInvokeAttributes.ThrowOnUnmappableCharMask); } /// /// true if is set /// public bool IsThrowOnUnmappableCharUseAssem => ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharUseAssem; /// /// true if is set /// public bool IsThrowOnUnmappableCharEnabled => ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharEnabled; /// /// true if is set /// public bool IsThrowOnUnmappableCharDisabled => ((PInvokeAttributes)attributes & PInvokeAttributes.ThrowOnUnmappableCharMask) == PInvokeAttributes.ThrowOnUnmappableCharDisabled; /// /// Gets/sets the bit /// public bool SupportsLastError { get => ((PInvokeAttributes)attributes & PInvokeAttributes.SupportsLastError) != 0; set => ModifyAttributes(value, PInvokeAttributes.SupportsLastError); } /// /// Gets/sets calling convention /// public PInvokeAttributes CallConv { get => (PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask; set => ModifyAttributes(~PInvokeAttributes.CallConvMask, value & PInvokeAttributes.CallConvMask); } /// /// true if is set /// public bool IsCallConvWinapi => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvWinapi; /// /// true if is set /// public bool IsCallConvCdecl => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvCdecl; /// /// true if is set /// public bool IsCallConvStdcall => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvStdcall; /// /// true if is set /// public bool IsCallConvThiscall => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvThiscall; /// /// true if is set /// public bool IsCallConvFastcall => ((PInvokeAttributes)attributes & PInvokeAttributes.CallConvMask) == PInvokeAttributes.CallConvFastcall; /// /// Checks whether this is a certain P/Invoke method /// /// Name of the DLL /// Name of the function within the DLL /// true if it's the specified P/Invoke method, else false public bool IsPinvokeMethod(string dllName, string funcName) => IsPinvokeMethod(dllName, funcName, IsWindows()); /// /// Checks whether this is a certain P/Invoke method /// /// Name of the DLL /// Name of the function within the DLL /// Treat as Windows /// true if it's the specified P/Invoke method, else false public bool IsPinvokeMethod(string dllName, string funcName, bool treatAsWindows) { if (name != funcName) return false; var mod = module; if (mod is null) return false; return GetDllName(dllName, treatAsWindows).Equals(GetDllName(mod.Name, treatAsWindows), StringComparison.OrdinalIgnoreCase); } static string GetDllName(string dllName, bool treatAsWindows) { if (treatAsWindows) dllName = dllName.TrimEnd(trimChars); if (dllName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) return dllName.Substring(0, dllName.Length - 4); return dllName; } static bool IsWindows() => #if NETSTANDARD || NETCOREAPP RuntimeInformation.IsOSPlatform(OSPlatform.Windows); #else Path.DirectorySeparatorChar == '\\' || Path.AltDirectorySeparatorChar == '\\'; #endif static readonly char[] trimChars = { ' ' }; } /// /// An ImplMap row created by the user and not present in the original .NET file /// public class ImplMapUser : ImplMap { /// /// Default constructor /// public ImplMapUser() { } /// /// Constructor /// /// Scope /// Name /// Flags public ImplMapUser(ModuleRef scope, UTF8String name, PInvokeAttributes flags) { module = scope; this.name = name; attributes = (int)flags; } } /// /// Created from a row in the ImplMap table /// sealed class ImplMapMD : ImplMap, IMDTokenProviderMD { readonly uint origRid; /// public uint OrigRid => origRid; /// /// Constructor /// /// The module which contains this ImplMap row /// Row ID /// If is null /// If is invalid public ImplMapMD(ModuleDefMD readerModule, uint rid) { #if DEBUG if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.ImplMapTable.IsInvalidRID(rid)) throw new BadImageFormatException($"ImplMap rid {rid} does not exist"); #endif origRid = rid; this.rid = rid; bool b = readerModule.TablesStream.TryReadImplMapRow(origRid, out var row); Debug.Assert(b); attributes = row.MappingFlags; name = readerModule.StringsStream.ReadNoNull(row.ImportName); module = readerModule.ResolveModuleRef(row.ImportScope); } } }