Search code examples
c#.netvb.netdlldbghelp

'SymEnumSymbols' API retrieves only the First letter of each method name


I was using this solution to retrieve method names from an unmanaged C++ library, the solution uses SymEnumerateSymbols64 function but MSDN says here that applications must use SymEnumSymbols instead, so I've found this other solution, but when I've tried to adapt and translate the C# code into VB.NET code the SYMBOL_INFO.Name property only contains the first letter of the method name.

Where is the problem and how I can fix it?.

I think that the problem could be the declaration inside the structure:

<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=1024I)>
Public Name As String

...But if I remove the marshalling, the application just closes itself without any kind of exception.

This is the full code:

<DllImport("dbghelp.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Public Shared Function SymInitialize(
       ByVal hProcess As IntPtr,
       ByVal UserSearchPath As String,
      <MarshalAs(UnmanagedType.Bool)> ByVal fInvadeProcess As Boolean
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

<DllImport("dbghelp.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Public Shared Function SymCleanup(
       ByVal hProcess As IntPtr
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

<DllImport("dbghelp.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Public Shared Function SymLoadModuleEx(
       ByVal hProcess As IntPtr,
       ByVal hFile As IntPtr,
       ByVal ImageName As String,
       ByVal ModuleName As String,
       ByVal BaseOfDll As Long,
       ByVal DllSize As Integer,
       ByVal Data As IntPtr,
       ByVal Flags As SymLoadModuleFlags
) As ULong
End Function

<DllImport("dbghelp.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Public Shared Function SymEnumSymbols(
   ByVal hProcess As IntPtr,
   ByVal BaseOfDll As ULong,
   ByVal Mask As String,
   ByVal EnumSymbolsCallback As SymEnumSymbolsProc,
   ByVal UserContext As IntPtr
) As Boolean '  As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

Public Delegate Function SymEnumSymbolsProc(
       ByRef pSymInfo As SYMBOL_INFO,
       ByVal SymbolSize As UInteger,
       ByVal UserContext As IntPtr
) As Boolean

<StructLayout(LayoutKind.Sequential)>
Public Structure SYMBOL_INFO

    Public SizeOfStruct As UInteger
    Public TypeIndex As UInteger
    Public Reserved1 As ULong
    Public Reserved2 As ULong
    Public Reserved3 As UInteger
    Public Size As UInteger
    Public ModBase As ULong
    Public Flags As SymFlag
    Public Value As ULong
    Public Address As ULong
    Public Register As UInteger
    Public Scope As UInteger
    Public Tag As SymTagEnum
    Public NameLen As Integer
    Public MaxNameLen As Integer
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=1024)>
    Public Name As String

End Structure

<FlagsAttribute>
Public Enum SymFlag As UInteger

    VALUEPRESENT = &H1
    REGISTER = &H8
    REGREL = &H10
    FRAMEREL = &H20
    PARAMETER = &H40
    LOCAL = &H80
    CONSTANT = &H100
    EXPORT = &H200
    FORWARDER = &H400
    [FUNCTION] = &H800
    VIRTUAL = &H1000
    THUNK = &H2000
    TLSREL = &H4000

End Enum

<FlagsAttribute>
Public Enum SymTagEnum As UInteger
    Null
    Exe
    Compiland
    CompilandDetails
    CompilandEnv
    [Function]
    Block
    Data
    Annotation
    Label
    PublicSymbol
    UDT
    [Enum]
    FunctionType
    PointerType
    ArrayType
    BaseType
    Typedef
    BaseClass
    [Friend]
    FunctionArgType
    FuncDebugStart
    FuncDebugEnd
    UsingNamespace
    VTableShape
    VTable
    [Custom]
    Thunk
    CustomType
    ManagedType
    Dimension
End Enum

<Description("Enum used as 'Flags' parameter of 'SymLoadModuleEx' function")>
<FlagsAttribute()>
Public Enum SymLoadModuleFlags As Integer

    Module_And_Symbols = &H0I
    Only_Module = &H4I
    Virtual = &H1I

End Enum

Public Shared Function EnumSyms(ByRef pSymInfo As SYMBOL_INFO,
                                ByVal SymbolSize As UInteger,
                                ByVal UserContext As IntPtr) As Boolean

    '  Debug.WriteLine(pSymInfo.Name)
    Debug.WriteLine(pSymInfo.Address & " " & SymbolSize & " " & pSymInfo.Name)
    Return True

End Function

And this is a real example usage:

Private Sub Test() Handles MyBase.Shown

    Dim hCurrentProcess As IntPtr = Process.GetCurrentProcess().Handle
    Dim baseOfDll As ULong
    Dim status As Boolean

    ' Initialize sym.
    ' Please read the remarks on MSDN for the hProcess
    ' parameter.
    status = SymInitialize(hCurrentProcess, Nothing, False)

    If status = False Then
        MsgBox("Failed to initialize sym.")
        Exit Sub
    End If

    ' Load dll.
    baseOfDll = SymLoadModuleEx(hCurrentProcess,
                                IntPtr.Zero,
                                "C:\Users\Administrador\Desktop\x64.dll",
                                Nothing,
                                0,
                                0,
                                IntPtr.Zero,
                                SymLoadModuleFlags.Module_And_Symbols)

    If baseOfDll = 0 Then
        MsgBox("Failed to load module.")
        SymCleanup(hCurrentProcess)
        Exit Sub
    End If

    ' Enumerate symbols. For every symbol the 
    ' callback method EnumSyms is called.
    If Not SymEnumSymbols(hCurrentProcess,
                          baseOfDll,
                          "*",
                          AddressOf EnumSyms,
                          IntPtr.Zero
    ) Then

        MsgBox("Failed to enum symbols.")

    End If

    ' Cleanup.
    SymCleanup(hCurrentProcess)

End Sub

Solution

  • I see a few issues with your code.

    SYMBOL_INFO

    Should be defined as a Class. A vb.net structure is not the same as a C++ structure.

    Public Reserved1 As ULong
    Public Reserved2 As ULong
    Public Reserved3 As UInteger
    

    There's only two reserved words, not three.

    ULONG64 Reserved[2];
    

    SymEnumSymbolsProc

    ByRef pSymInfo As SYMBOL_INFO
    

    Should be a pointer to a SYMBOL_INFO struct passed ByVal.

    _In_ PSYMBOL_INFO pSymInfo,
    



    Sample form

    Imports System.Runtime.InteropServices
    Imports System.ComponentModel
    
    Public Class Form1
    
        Private Sub HandleLoad(sender As Object, e As EventArgs) Handles MyBase.Load
    
            Dim [error] As Exception = Nothing
            Dim initialized As Boolean = False
            Dim hProcess As IntPtr = Nothing
    
            Try
    
                SymSetOptions(2 Or 4)
                hProcess = Process.GetCurrentProcess().Handle
    
                If (SymInitialize(hProcess, Nothing, True)) Then
                    initialized = True
                Else
                    Throw New Win32Exception(Marshal.GetLastWin32Error())
                End If
    
                Dim baseOfDll As IntPtr = SymLoadModuleEx(hProcess, IntPtr.Zero, (Environment.SystemDirectory & "\gdi32.dll"), Nothing, 0, 0, IntPtr.Zero, &H0I)
    
                If (baseOfDll = IntPtr.Zero) Then
                    Throw New Win32Exception(Marshal.GetLastWin32Error())
                End If
    
                If Not SymEnumSymbols(
                    hProcess,
                    baseOfDll,
                    "*",
                    Function(pSymInfo As IntPtr, SymbolSize As UInteger, UserContext As IntPtr)
                        Dim struct As New SYMBOL_INFO
                        struct.SizeOfStruct = Marshal.SizeOf(GetType(SYMBOL_INFO))
                        Marshal.PtrToStructure(pSymInfo, struct)
                        Debug.WriteLine(struct.Name)
                        Return True
                    End Function, IntPtr.Zero
                ) Then
                    Throw New Win32Exception(Marshal.GetLastWin32Error())
                End If
    
            Catch ex As Exception
                Debug.WriteLine(ex.Message)
            Finally
                If (initialized) Then
                    SymCleanup(hProcess)
                End If
            End Try
    
        End Sub
    
        <DllImport("dbghelp.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Private Shared Function SymSetOptions(ByVal SymOptions As Integer) As Integer
        End Function
    
        <DllImport("dbghelp.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Private Shared Function SymInitialize(ByVal hProcess As IntPtr, ByVal UserSearchPath As String, <MarshalAs(UnmanagedType.Bool)> ByVal fInvadeProcess As Boolean) As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function
    
        <DllImport("dbghelp.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Private Shared Function SymCleanup(ByVal hProcess As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function
    
        <DllImport("dbghelp.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Private Shared Function SymLoadModuleEx(ByVal hProcess As IntPtr, ByVal hFile As IntPtr, ByVal ImageName As String, ByVal ModuleName As String, ByVal BaseOfDll As Long, ByVal DllSize As Integer, ByVal Data As IntPtr, ByVal Flags As Integer) As ULong
        End Function
    
        <DllImport("dbghelp.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Private Shared Function SymEnumSymbols(ByVal hProcess As IntPtr, ByVal BaseOfDll As ULong, ByVal Mask As String, ByVal EnumSymbolsCallback As SymEnumSymbolsProc, ByVal UserContext As IntPtr) As Boolean '  As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function
    
        Private Delegate Function SymEnumSymbolsProc(pSymInfo As IntPtr, ByVal SymbolSize As UInteger, ByVal UserContext As IntPtr) As Boolean
    
        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
        Private Class SYMBOL_INFO
            Public SizeOfStruct As UInteger
            Public TypeIndex As UInteger
            Public Reserved1 As ULong
            Public Reserved2 As ULong
            Public Index As UInteger
            Public Size As UInteger
            Public ModBase As ULong
            Public Flags As UInteger
            Public Value As ULong
            Public Address As ULong
            Public Register As UInteger
            Public Scope As UInteger
            Public Tag As UInteger
            Public NameLen As UInteger
            Public MaxNameLen As UInteger
            <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=1024)> Public Name As String
        End Class
    
    End Class