Declaring this function:
<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
Based on the definition here: https://pinvoke.net/default.aspx/kernel32/FormatMessage.html
Scenario
I am currently PInvoking wininet.dll to perform FTP transactions against a server. If an error is encountered I get a error code back from Err.LastDllError. I have a function defined that gets the the dll error and returns a message based on the error code, however it is not working as expected. Here is the function that I use to throw dll errors:
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
Based on the technique described here: https://learn.microsoft.com/en-us/windows/desktop/wininet/appendix-c-handling-errors I am expecting to get some kind of string message based on the error code for this particular dll, for example if I get an error code of 12110 (ERROR_FTP_TRANSFER_IN_PROGRESS. Reference: https://support.microsoft.com/en-au/help/193625/info-wininet-error-codes-12001-through-12156) I would expect to get a message back (in variable sMessageBuffer) similar to the following if not the same "The requested operation cannot be made on the FTP session handle because an operation is already in progress.". However sMessageBuffer is never assigned a value and remains nothing. I can only assume I am misusing this technique somehow, I've tried various ways described on online forums and this site itself but I haven't been successful.
Here some sample code that works:
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