From 0edde0e5a23b92e2415df72fd0df6287f3290891 Mon Sep 17 00:00:00 2001 From: dahall Date: Tue, 3 May 2022 19:31:09 -0600 Subject: [PATCH] Fixed bug #294 -- problem with SYMBOL_INFO and SymEnumSymbols not showing Name field correctly. Had to change PSYM_ENUMERATESYMBOLS_CALLBACK (breaking change). Added new SymEnumSymbolsEx overload to do the hard work. --- PInvoke/DbgHelp/DbgHelp.Structs.cs | 40 +++++------ PInvoke/DbgHelp/DbgHelp.Sym.cs | 108 ++++++++++++++++++++++++++++-- UnitTests/PInvoke/DbgHelp/DbgHelpTests.cs | 11 +++ 3 files changed, 130 insertions(+), 29 deletions(-) diff --git a/PInvoke/DbgHelp/DbgHelp.Structs.cs b/PInvoke/DbgHelp/DbgHelp.Structs.cs index a9151f05..e30795d4 100644 --- a/PInvoke/DbgHelp/DbgHelp.Structs.cs +++ b/PInvoke/DbgHelp/DbgHelp.Structs.cs @@ -604,7 +604,7 @@ namespace Vanara.PInvoke { /// /// The size of the structure, in bytes. The caller must set this member to - /// sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64) + /// sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64) /// . /// public uint SizeOfStruct; @@ -655,7 +655,7 @@ namespace Vanara.PInvoke { /// /// The size of the structure, in bytes. The caller must set this member to - /// sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64) + /// sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64) /// . /// public uint SizeOfStruct; @@ -713,7 +713,7 @@ namespace Vanara.PInvoke { /// /// The size of the structure, in bytes. The caller must set this member to - /// sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64) + /// sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64) /// . /// public uint SizeOfStruct; @@ -771,7 +771,7 @@ namespace Vanara.PInvoke { /// /// The size of the structure, in bytes. The caller must set this member to - /// sizeof(IMAGEHLP_DUPLICATE_SYMBOL64) + /// sizeof(IMAGEHLP_DUPLICATE_SYMBOL64) /// . /// public uint SizeOfStruct; @@ -908,7 +908,7 @@ namespace Vanara.PInvoke { /// /// The size of the structure, in bytes. The caller must set this member to - /// sizeof(IMAGEHLP_LINE64) + /// sizeof(IMAGEHLP_LINE64) /// . /// public uint SizeOfStruct; @@ -944,7 +944,7 @@ namespace Vanara.PInvoke { /// /// The size of the structure, in bytes. The caller must set this member to - /// sizeof(IMAGEHLP_LINE64) + /// sizeof(IMAGEHLP_LINE64) /// . /// public uint SizeOfStruct; @@ -981,7 +981,7 @@ namespace Vanara.PInvoke { /// /// The size of the structure, in bytes. The caller must set this member to - /// sizeof(IMAGEHLP_MODULE64) + /// sizeof(IMAGEHLP_MODULE64) /// . /// public uint SizeOfStruct; @@ -1089,7 +1089,7 @@ namespace Vanara.PInvoke { /// /// The size of the structure, in bytes. The caller must set this member to - /// sizeof(IMAGEHLP_MODULE64) + /// sizeof(IMAGEHLP_MODULE64) /// . /// public uint SizeOfStruct; @@ -1403,7 +1403,6 @@ namespace Vanara.PInvoke } } - /// Contains symbol information. /// /// @@ -2089,7 +2088,7 @@ namespace Vanara.PInvoke /// /// Set to - /// sizeof(STACKFRAME_EX) + /// sizeof(STACKFRAME_EX) /// . /// public uint StackFrameSize; @@ -2177,15 +2176,14 @@ namespace Vanara.PInvoke // SizeOfStruct; ULONG TypeIndex; ULONG64 Reserved[2]; ULONG Index; ULONG Size; ULONG64 ModBase; ULONG Flags; ULONG64 Value; ULONG64 // Address; ULONG Register; ULONG Scope; ULONG Tag; ULONG NameLen; ULONG MaxNameLen; CHAR Name[1]; } SYMBOL_INFO, *PSYMBOL_INFO; [PInvokeData("dbghelp.h", MSDNShortId = "NS:dbghelp._SYMBOL_INFO")] + [VanaraMarshaler(typeof(SafeAnysizeStringMarshaler), "Auto")] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct SYMBOL_INFO { /// - /// The size of the structure, in bytes. This member must be set to - /// sizeof(SYMBOL_INFO) - /// . Note that the total size of the data is the - /// SizeOfStruct + (MaxNameLen - 1) * sizeof(TCHAR) - /// . The reason to subtract one is that the first character in the name is accounted for in the size of the structure. + /// The size of the structure, in bytes. This member must be set to sizeof(SYMBOL_INFO) . Note that the total size of the + /// data is the SizeOfStruct + (MaxNameLen - 1) * sizeof(TCHAR) . The reason to subtract one is that the first character + /// in the name is accounted for in the size of the structure. /// public uint SizeOfStruct; @@ -2310,11 +2308,8 @@ namespace Vanara.PInvoke public uint Register; /// - /// /// The DIA scope. For more information, see the Debug Interface Access SDK in the Visual Studio documentation. (This resource - /// may not be available in some languages - /// - /// and countries.) + /// may not be available in some languages and countries.) /// public uint Scope; @@ -2343,11 +2338,8 @@ namespace Vanara.PInvoke public struct SYMSRV_INDEX_INFO { /// - /// The size of the structure, in bytes. This member must be set to - /// sizeof(SYMSRV_INDEX_INFO) - /// or - /// sizeof(SYMSRV_INDEX_INFOW) - /// . + /// The size of the structure, in bytes. This member must be set to sizeof(SYMSRV_INDEX_INFO) or + /// sizeof(SYMSRV_INDEX_INFOW) . /// public uint sizeofstruct; diff --git a/PInvoke/DbgHelp/DbgHelp.Sym.cs b/PInvoke/DbgHelp/DbgHelp.Sym.cs index b2fdea30..4ea49f2f 100644 --- a/PInvoke/DbgHelp/DbgHelp.Sym.cs +++ b/PInvoke/DbgHelp/DbgHelp.Sym.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; +using Vanara.Extensions; using Vanara.InteropServices; namespace Vanara.PInvoke @@ -88,7 +89,7 @@ namespace Vanara.PInvoke // SymbolSize, PVOID UserContext ) {...} [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Auto)] [PInvokeData("dbghelp.h", MSDNShortId = "NC:dbghelp.PSYM_ENUMERATESYMBOLS_CALLBACK")] - public delegate bool PSYM_ENUMERATESYMBOLS_CALLBACK(in SYMBOL_INFO pSymInfo, uint SymbolSize, [In, Optional] IntPtr UserContext); + public delegate bool PSYM_ENUMERATESYMBOLS_CALLBACK([In] IntPtr pSymInfo, uint SymbolSize, [In, Optional] IntPtr UserContext); /// /// An application-defined callback function used with the SymEnumLines and SymEnumSourceLines functions. @@ -1826,9 +1827,105 @@ namespace Vanara.PInvoke [DllImport(Lib_DbgHelp, SetLastError = true, CharSet = CharSet.Auto)] [PInvokeData("dbghelp.h", MSDNShortId = "NF:dbghelp.SymEnumSymbols")] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool SymEnumSymbols(HPROCESS hProcess, ulong BaseOfDll, [Optional, MarshalAs(UnmanagedType.LPTStr)] string Mask, + public static extern bool SymEnumSymbols(HPROCESS hProcess, [Optional] ulong BaseOfDll, [Optional, MarshalAs(UnmanagedType.LPTStr)] string Mask, PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, [In, Optional] IntPtr UserContext); + /// Enumerates all symbols in a process. + /// A handle to a process. This handle must have been previously passed to the SymInitialize function. + /// + /// The base address of the module. If this value is zero and Mask contains an exclamation point (!), the function looks across + /// modules. If this value is zero and Mask does not contain an exclamation point, the function uses the scope established by the + /// SymSetContext function. + /// + /// + /// + /// A wildcard string that indicates the names of the symbols to be enumerated. The text can optionally contain the wildcards, "*" + /// and "?". + /// + /// + /// To specify a specific module or set of modules, begin the text with a wildcard string specifying the module, followed by an + /// exclamation point. When specifying a module, BaseOfDll is ignored. + /// + /// + /// + /// Value + /// Meaning + /// + /// + /// foo + /// + /// If BaseOfDll is not zero, then SymEnumSymbols will look for a global symbol named "foo". If BaseOfDll is zero, then + /// SymEnumSymbols will look for a local symbol named "foo" within the scope established by the most recent call to the SymSetContext function. + /// + /// + /// + /// foo? + /// + /// If BaseOfDll is not zero, then SymEnumSymbols will look for a global symbol that starts with "foo" and contains one extra + /// character afterwards, such as "fool" and "foot". If BaseOfDll is zero, then SymEnumSymbols will look for a symbol that starts + /// with "foo" and contains one extra character afterwards, such as "fool" and "foot". The search would be within the scope + /// established by the most recent call to the SymSetContext function. + /// + /// + /// + /// foo*!bar + /// + /// SymEnumSymbols will look in every loaded module that starts with the text "foo" for a symbol called "bar". It could find matches + /// such as these, "foot!bar", "footlocker!bar", and "fool!bar". + /// + /// + /// + /// *!* + /// SymEnumSymbols will enumerate every symbol in every loaded module. + /// + /// + /// + /// + /// Indicates possible options. + /// + /// + /// Value + /// Meaning + /// + /// + /// SYMENUM_OPTIONS_DEFAULT 1 + /// Use the default options. + /// + /// + /// SYMENUM_OPTIONS_INLINE 2 + /// Enumerate inline symbols. + /// + /// + /// + /// + /// A user-defined value that is passed to the callback function, or NULL. This parameter is typically used by an application + /// to pass a pointer to a data structure that provides context for the callback function. + /// + /// A list of structures. + /// + /// + /// All DbgHelp functions, such as this one, are single threaded. Therefore, calls from more than one thread to this function will + /// likely result in unexpected behavior or memory corruption. To avoid this, you must synchronize all concurrent calls from more + /// than one thread to this function. + /// + /// Examples + /// For an example, see Enumerating Symbols. + /// + public static IList SymEnumSymbolsEx(HPROCESS hProcess, [Optional] ulong BaseOfDll, [Optional, MarshalAs(UnmanagedType.LPTStr)] string Mask, + SYMENUM Options = SYMENUM.SYMENUM_OPTIONS_DEFAULT, [In] IntPtr UserContext = default) + { + List list = new(); + Win32Error.ThrowLastErrorIfFalse(SymEnumSymbolsEx(hProcess, BaseOfDll, Mask, EnumProc, UserContext, Options)); + return list; + + bool EnumProc(IntPtr pSymInfo, uint SymbolSize, IntPtr UserContext) + { + try { list.Add(pSymInfo.ToStructure(SymbolSize)); return true; } + catch { } + return false; + } + } + /// Enumerates all symbols in a process. /// A handle to a process. This handle must have been previously passed to the SymInitialize function. /// @@ -1911,7 +2008,7 @@ namespace Vanara.PInvoke [DllImport(Lib_DbgHelp, SetLastError = true, CharSet = CharSet.Auto)] [PInvokeData("dbghelp.h", MSDNShortId = "NF:dbghelp.SymEnumSymbolsEx")] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool SymEnumSymbolsEx(HPROCESS hProcess, ulong BaseOfDll, [Optional, MarshalAs(UnmanagedType.LPTStr)] string Mask, + public static extern bool SymEnumSymbolsEx(HPROCESS hProcess, [Optional] ulong BaseOfDll, [Optional, MarshalAs(UnmanagedType.LPTStr)] string Mask, PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, [In, Optional] IntPtr UserContext, SYMENUM Options); /// Enumerates the symbols for the specified address. @@ -4480,7 +4577,7 @@ namespace Vanara.PInvoke [DllImport(Lib_DbgHelp, SetLastError = true, CharSet = CharSet.Auto)] [PInvokeData("dbghelp.h", MSDNShortId = "NF:dbghelp.SymLoadModuleEx")] public static extern ulong SymLoadModuleEx(HPROCESS hProcess, [Optional] HFILE hFile, [Optional, MarshalAs(UnmanagedType.LPTStr)] string ImageName, - [Optional, MarshalAs(UnmanagedType.LPTStr)] string ModuleName, ulong BaseOfDll, uint DllSize, in MODLOAD_DATA Data, SLMFLAG Flags); + [Optional, MarshalAs(UnmanagedType.LPTStr)] string ModuleName, ulong BaseOfDll, uint DllSize, in MODLOAD_DATA Data, [Optional] SLMFLAG Flags); /// Loads the symbol table for the specified module. /// A handle to the process that was originally passed to the SymInitialize function. @@ -4566,7 +4663,8 @@ namespace Vanara.PInvoke [DllImport(Lib_DbgHelp, SetLastError = true, CharSet = CharSet.Auto)] [PInvokeData("dbghelp.h", MSDNShortId = "NF:dbghelp.SymLoadModuleEx")] public static extern ulong SymLoadModuleEx(HPROCESS hProcess, [Optional] HFILE hFile, [Optional, MarshalAs(UnmanagedType.LPTStr)] string ImageName, - [Optional, MarshalAs(UnmanagedType.LPTStr)] string ModuleName, ulong BaseOfDll, uint DllSize, [In, Optional] IntPtr Data, SLMFLAG Flags); + [Optional, MarshalAs(UnmanagedType.LPTStr)] string ModuleName, [Optional] ulong BaseOfDll, [Optional] uint DllSize, [In, Optional] IntPtr Data, + [Optional] SLMFLAG Flags); /// Compares a string to a file name and path. /// The file name to be compared to the Match parameter. diff --git a/UnitTests/PInvoke/DbgHelp/DbgHelpTests.cs b/UnitTests/PInvoke/DbgHelp/DbgHelpTests.cs index 0ec1cb27..aa629050 100644 --- a/UnitTests/PInvoke/DbgHelp/DbgHelpTests.cs +++ b/UnitTests/PInvoke/DbgHelp/DbgHelpTests.cs @@ -122,6 +122,17 @@ namespace Vanara.PInvoke.Tests Assert.That(SymEnumProcesses(), Is.Not.Empty); } + [Test] + public void SymEnumSymbolsExTest() + { + using var fakeProc = new ProcessSymbolHandler(new IntPtr(1), null, false); + var pdbBase = SymLoadModuleEx(fakeProc, default, @"C:\Windows\System32\ntdll.dll"); + var list = SymEnumSymbolsEx(fakeProc, pdbBase); + Assert.That(list, Is.Not.Empty); + foreach (var i in list) + TestContext.WriteLine(i.Name); + } + [Test] public unsafe void SymGetOmapsTest() {