PInvoke Сообщение FormatMessage ByRef lpBuffer As [String] - Ничто - PullRequest
2 голосов
/ 03 июля 2019

Объявление этой функции:

    <DllImport("kernel32.dll", EntryPoint:="FormatMessageW", SetLastError:=True, CharSet:=CharSet.Unicode, CallingConvention:=CallingConvention.StdCall)>
Public Shared Function FormatMessage(ByVal dwFlags As Integer,
                                     ByRef lpSource As IntPtr,
                                     ByVal dwMessageId As Integer,
                                     ByVal dwLanguageId As Integer,
                                     ByRef lpBuffer As [String],
                                     ByVal nSize As Integer,
                                     ByRef Arguments As IntPtr) As Integer
End Function

На основании определения здесь: https://pinvoke.net/default.aspx/kernel32/FormatMessage.html

Сценарий
Я в настоящее время PInvoking wininet.dll для выполнения транзакций FTP против сервера. Если возникает ошибка, я получаю код ошибки от Err.LastDllError . У меня есть определенная функция, которая получает ошибку DLL и возвращает сообщение на основе кода ошибки, однако она не работает, как ожидалось. Вот функция, которую я использую, чтобы выдавать ошибки dll:

Private Sub ThrowLastDllError()
    Dim iLastErrorID As Integer = Err.LastDllError
    Dim iMessageBuffer As IntPtr = Nothing
    Dim iModuleHandle As IntPtr = GetModuleHandle("wininet.dll")

    Dim sMessageBuffer As String = Nothing

    If iLastErrorID > 12000 And iLastErrorID < 12157 Then
        FormatMessage(FORMAT_MESSAGE_FROM_HMODULE Or
                     FORMAT_MESSAGE_IGNORE_INSERTS Or
                     FORMAT_MESSAGE_ALLOCATE_BUFFER,
                     iModuleHandle,
                     iLastErrorID,
                     0,
                     sMessageBuffer,
                     256,
                     Nothing)

        Debugger.Break()
        'TODO: Throw exception with error code message here
    End If
End Sub

На основе описанной здесь техники: https://docs.microsoft.com/en-us/windows/desktop/wininet/appendix-c-handling-errors Я ожидаю получить некое строковое сообщение на основе кода ошибки для этой конкретной библиотеки DLL, например, если я получу код ошибки 12110 (ERROR_FTP_TRANSFER_IN_PROGRESS. Ссылка : https://support.microsoft.com/en-au/help/193625/info-wininet-error-codes-12001-through-12156) Я ожидал бы получить сообщение обратно (в переменной sMessageBuffer), похожее на следующее, если не то же самое «Запрошенная операция не может быть выполнена на дескрипторе сеанса FTP, потому что операция уже выполняется». Однако sMessageBuffer никогда не присваивается значение и ничего не остается. Я могу только предположить, что каким-то образом неправильно использую эту технику, я пробовал различные способы, описанные на онлайн-форумах и на этом сайте, но у меня ничего не получилось.

1 Ответ

2 голосов
/ 03 июля 2019

Вот пример кода, который работает:

Sub Main()

    Dim h As IntPtr = LoadLibrary("wininet.dll") ' or GetModuleHandle ...
    Dim sb = New StringBuilder(1024)
    FormatMessage(Format_Message.FORMAT_MESSAGE_FROM_HMODULE Or Format_Message.FORMAT_MESSAGE_IGNORE_INSERTS,
        h,
        12002,
        0,
        sb,
        sb.Capacity,
        Nothing)

    Console.WriteLine(sb) ' prints "The operation timed out"
   ' FreeLibrary, etc.
End Sub

Enum Format_Message
    FORMAT_MESSAGE_IGNORE_INSERTS = &H200
    FORMAT_MESSAGE_FROM_SYSTEM = &H1000
    FORMAT_MESSAGE_FROM_HMODULE = &H800
End Enum

<DllImport("Kernel32", SetLastError:=True, CharSet:=CharSet.Unicode)>
Public Function FormatMessage(ByVal dwFlags As Format_Message, ByVal lpSource As IntPtr, ByVal dwMessageId As Integer, ByVal dwLanguageId As Integer, lpBuffer As StringBuilder, ByVal nSize As Integer, ByVal Arguments As IntPtr) As Integer
End Function

<DllImport("kernel32", SetLastError:=True, CharSet:=CharSet.Unicode)>
Public Function LoadLibrary(ByVal lpFileName As String) As IntPtr
End Function
...