Vb.net PInvoke FormatMessage消息ByRef lpBuffer As[String]为空

Vb.net PInvoke FormatMessage消息ByRef lpBuffer As[String]为空,vb.net,pinvoke,wininet,kernel32,Vb.net,Pinvoke,Wininet,Kernel32,声明此函数: <DllImport("kernel32.dll", EntryPoint:="FormatMessageW", SetLastError:=True, CharSet:=CharSet.Unicode, CallingConvention:=CallingConvention.StdCall)> Public Shared Function FormatMessage(ByVal dwFlags As Integer,

声明此函数:

    <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

公共共享函数FormatMessage(ByVal dwFlags为整数,
ByRef lpSource作为IntPtr,
ByVal dwMessageId为整数,
ByVal dwLanguageId为整数,
ByRef lpBuffer作为[字符串],
ByVal nSize为整数,
ByRef参数作为IntPtr)作为整数
端函数
根据此处的定义:

情景
我目前正在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
Private Sub-ThrowLastDllError()
Dim iLastErrorID为整数=Err.lastdlerror
Dim iMessageBuffer作为IntPtr=无
Dim iModuleHandle作为IntPtr=GetModuleHandle(“wininet.dll”)
Dim sMessageBuffer作为字符串=无
如果iLastErrorID>12000且iLastErrorID<12157,则
格式化消息(格式化来自模块的消息或
格式化\u消息\u忽略\u插入或

格式化\u消息\u分配\u缓冲区, 伊莫拉多, 伊拉斯特罗里德, 0, sMessageBuffer, 256, (没有) Debugger.Break() 'TODO:在此处引发异常并显示错误代码消息 如果结束 端接头

基于这里描述的技术:我希望根据这个特定dll的错误代码得到某种字符串消息,例如,如果我得到的错误代码是12110(error_FTP_TRANSFER_IN_PROGRESS.Reference:),我希望得到一条类似于下面的消息(在变量sMessageBuffer中),如果不是相同的话“无法对FTP会话句柄执行请求的操作,因为操作已在进行中。“。但是,sMessageBuffer从未分配值,并且保持为空。我只能假设我不知何故误用了这项技术,我尝试过在线论坛和本网站上描述的各种方法,但都没有成功。

以下是一些有效的示例代码:

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
Sub-Main()
Dim h为IntPtr=LoadLibrary(“wininet.dll”)或GetModuleHandle。。。
尺寸sb=新的StringBuilder(1024)
FormatMessage(Format_Message.Format_Message_FROM_HMODULE或Format_Message.Format_Message_IGNORE_insert,
H
12002,
0,
某人,
某人的能力,
(没有)
Console.WriteLine(sb)“打印”操作超时”
“免费图书馆等。
端接头
枚举格式消息
格式\消息\忽略\插入=&H200
格式化来自系统的消息(&H1000)
格式化来自模块的消息(&H800)
结束枚举
公共函数FormatMessage(ByVal dwFlags作为Format_Message,ByVal lpSource作为IntPtr,ByVal dwMessageId作为Integer,ByVal dwLanguageId作为Integer,lpBuffer作为StringBuilder,ByVal nSize作为Integer,ByVal参数作为IntPtr)作为Integer
端函数
公共函数LoadLibrary(ByVal lpFileName作为字符串)作为IntPtr
端函数

尝试删除FORMAT\u MESSAGE\u ALLOCATE\u BUFFER标志,并使用带有StringBuildPerformat\u MESSAGE\u ALLOCATE\u BUFFER的签名传递预先分配的字符串是可以的,但您需要参数为ByRef IntPtr。然后手动封送字符串。最后,您需要通过调用Marshal.FreeHGlobal来释放它。@SimonMourier我现在使用带有StringBuilder参数的函数得到一个AccessViolationException,下面是它的实现:忽略InfWinAPITools.*stuff,编辑失败。@DavidHeffernan您是说lpBuffer是作为ByRef的IntPtr数据类型吗?这与文档不符。你对C#满意吗?谢谢,这给了我正确的结果。我没有将ByVal作为lpSource的参数,也没有忽略lpBuffer的ByRef,只要代码按预期工作。据我所知,PInvoke文档需要更新,因为我从那里得到了声明(这是不正确的)。当我的问题得到解决时,我很好奇你是如何得出这个结论的,文档中是否有我遗漏的东西?我只是想知道您针对这个特定问题采取了哪些故障排除步骤,以便将来我可以做同样的事情。您可以使用FORMAT\u MESSAGE\u ALLOCATE\u BUFFER,但托管代码IMHO更复杂。我只是尝试了pinvoke.net声明,检查它是否崩溃,就像你一样,所以我将lpBuffer更改为byval,因为它只是一个指针,而不是指向指针的指针。顺便说一句,FormatMessage是一个非常古老的函数(从Windows NT第1天开始),有一个令人讨厌的“瑞士刀”接口。@D.Foley ByRef IntPtr如果被调用方正在分配缓冲区,则是正确的。你似乎很难相信这一点。