Unmanaged call - Attempted to read or write protected memory - vb.net

I'm trying to call the following unmanaged dll and it returns an error:
Attempted to read or write protected memory
' this is the declare part
Declare Ansi Function GuestCard Lib "C:\proRFL.Dll" (ByVal fUSB As Byte,
ByVal dlsCoID As Long, ByVal CardNo As Integer, ByVal dai As Integer,
ByVal llock As Integer, ByVal pdoors As Integer, ByVal BTime As String,
ByVal ETime As String, ByVal LockNo As String, ByVal Buffer As String) As Integer
' this is the calling part
Dim st As Integer = GuestCard(flagUSB, CLng(dlsCoID), CInt(CardNo) Mod 16, CInt(dai) Mod 256, llock, pdoors, BTime, ETime, LockNo, bufHexStr)
What have I done wrong?

Found the problem. I should declare the function in a module instead of a class. A simple yet stupid mistake. Thank you all for helping me out. #GSerg You are right about the type. Tank you.

Related

VB.NET 2008 or higher: Memory violation reading STDIN via winapi

I am trying to port a VB6 application to VB.NET.
This is a console application that reads data from STDIN, modifies the data and writes it to STDOUT.
Google is my friend, so I have spend days now on finding a solution.
This search provided me with several versions of the WinApi ReadFile and I tried them all.
Also I have tried several versions of .NET.
However.....In every combination that I have tried, I keep getting memory violation errors.
Errors differ a little bit depending on the ReadFile variant im using, but the are all similar.
See for instance this variant of the error:
makeDataFilter < Test.in > Test.Out
Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at makeDataFilter.Filter_IO.ReadFile(Int64 hFile, String& lpBuffer, Int64 nNumberOfBytesToRead, Int64 lpNumberOfBytesRead, String& lpOverlapped)
at makeDataFilter.Filter_IO.GetData() in C:\Projects\.NET 2019\makeDataFilter\Filter_IO.vb:line 229
at makeDataFilter.Filter_IO.Main() in C:\Projects\.NET 2019\makeDataFilter\Filter_IO.vb:line 118
I use this to get a handle:
Public Const STD_INPUT_HANDLE = -10&
Public Const STD_OUTPUT_HANDLE = -11&
' Open en handle to StdIn or StdOut
Private Declare Function GetStdHandle Lib "kernel32.dll" (
ByVal nStdHandle As Int32
) As IntPtr
Here is the variant of the ReadFile API call that produces the error above:
<DllImport("kernel32")>
Declare Function ReadFile(
ByVal hFile As Long,
lpBuffer As String,
nNumberOfBytesToRead As Long,
lpNumberOfBytesRead As Long,
lpOverlapped As String
) As String
And here are all the other variations that I found and tried. All give similar errors:
' Read from Stdin
'Declare Function ReadFile Lib "kernel32.dll" (
' ByVal hFile As Long,
' lpBuffer As Any,
' ByVal nNumberOfBytesToRead As Long,
' lpNumberOfBytesRead As Long,
' lpOverlapped As Any
') As Long
'Declare Function ReadFile Lib "kernel32" (
' ByVal hFile As Long,
' lpBuffer As String,
' ByVal nNumberOfBytesToRead As Long,
' lpNumberOfBytesRead As Long,
' ByVal lpOverlapped As String
') As Long
'Declare Auto Function ReadFile Lib "Kernel32.dll" (
' ByVal hndRef As Integer,
' ByVal lpBuffer As StringBuilder,
' ByVal numberOfBytesToRead As Integer,
' ByRef numberOfBytesRead As Integer,
' ByVal flag As Integer
') As Boolean
'Declare Function ReadFile Lib "kernel32" (
' ByVal hFile As Integer,
' ByVal lpBuffer As Integer,
' ByVal nNumberOfBytesToRead As Integer,
' lpNumberOfBytesRead As Integer,
' ByVal lpOverlapped As Integer
') As Integer
The way I use it in my code is here:
Sub GetData()
Dim sBuff As String
Dim lBytesRead As Long
Dim rc As Long
lContentLength = 256
sBuff = lContentLength
Do
rc = ReadFile(hStdIn, sBuff, lContentLength, lBytesRead, 0&)
sFilterData &= Left$(sBuff, lBytesRead)
Loop While rc = 1
End Sub
Can somebody please assist me in finding a working way to read from STDIN and write to STDOUT in VB.NET please?
I am wondering...
The .NET Framework provides a System.Console class, which contains functionality for reading/writing the standard input/output streams. Is there a special reason for not using that functionality?
I guess your GetData method could be rewritten to something like this:
Sub GetData()
sFilterData = Console.In.ReadToEnd()
End Sub
However, it might be more efficient (especially for memory usage) to process the input stream data "on the fly" instead of reading and storing the entire input stream into a string first and then start processing.

Why does capGetDriverDescription accept a buffer as a value type instead of a reference type?

Trying out some API programming, I encountered a problem with the capGetDriverDescription function of AVICAP32.dll:
Declare Function capGetDriverDescriptionA Lib "avicap32.dll" (ByVal wDriver As Short, _
ByVal lpszName As String, ByVal cbName As Integer, ByVal lpszVer As String, _
ByVal cbVer As Integer) As Boolean
As far as I know, the lpszName parameter is a buffer for getting the driver description, and I read somewhere that the buffer should be passed as a reference type (ByRef instead of ByVal), and of course it should be a reference because of storing the information I need after returning from the function. But in this function, it's a value type, and it works fine!
even I tried to change it to ByRef but the application didnt run!
What knowledge am I missing? How can this buffer store my information while it's being passed a value type?
This is how the code calls the function to get available webcams:
Private Sub LoadDeviceList()
On Error Resume Next
Dim strName As String = Space(100)
Dim strVer As String = Space(100)
Dim bReturn As Boolean
Dim x As Integer = 0
Do
bReturn = capGetDriverDescriptionA(x, strName, 100, strVer, 100)
If bReturn Then
lst1.Items.Add(strName.Trim)
End If
x += 1
Application.DoEvents()
Loop Until bReturn = False
End Sub
I'll start by saying I don't know much about this subject (marshaling data can be a deep subject), but I think Default Marshaling for Strings (MSDN) may help you. Scroll down to the part that says Fixed-Length String Buffers. According to this, when a string is marshaled to the API, even if passed byRef, it can't be modified by the callee.
I haven't tested, but according to the MSDN example, to get a value back from your function, the definition would become
Declare Function capGetDriverDescriptionA Lib "avicap32.dll" (ByVal wDriver As Short, _
ByVal lpszName As StringBuilder, ByVal cbName As Integer, ByVal lpszVer As String, _
ByVal cbVer As Integer) As Boolean
Changing lpszName from String to StringBuilder. Apparently StringBuilder will work as a buffer to get the string back from the function.
Then you would call your function like so,
StringBuilder sb = new StringBuilder(256)
capGetDriverDescription(Driver, sb, sb.Capacity + 1, ....
return sb.ToString()
I found a very similar question, How do I import and call unmanaged C dll with ANSI C string "char *" pointer string from VB.NET?.

how to call TrackPopupMenu function from managed vb.net code

I am trying to call TrackPopupMenu function to display a menu at runtime from managed VB.NET code.
Below is the error I am getting :
PInvokeStackImbalance was detected Message: A call to PInvoke function
'UeWIPopupX!UeWIPopupX.mDeclares::TrackPopupMenu' has unbalanced
the stack. This is likely because the managed PInvoke signature does
not match the unmanaged target signature. Check that the calling
convention and parameters of the PInvoke signature match the target
unmanaged signature.
Below is the declaration I am using for TrackPopupMenu function :
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True, CallingConvention:=CallingConvention.StdCall)> _
Friend Function TrackPopupMenu(ByVal hMenu As Long, ByVal wFlags As Integer, ByVal x As Integer, ByVal y As Integer, ByVal nReserved As Integer, ByVal hWnd As IntPtr, ByVal lprc As RECT) As Integer
End Function
Below is the code for calling TrackPopupMenu function :
dim lpRc as RECT
Dim tP As POINTAPI
Dim lR as Integer
Dim lUn as Integer
lUn = TPM_RIGHTBUTTON Or TPM_TOPALIGN Or TPM_LEFTALIGN Or TPM_RETURNCMD
tP.x = 50
tP.y = 100
'Here I am getting the error
lR = TrackPopupMenu(m_ppMenu.Tools(1).hMenu, lUn, tP.x, tP.y, 0, m_hWndOwner, lpRC)
Below is the declaration for rectangle RECT:
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Structure RECT
Dim Left As Integer
Dim Top As Integer
Dim Right As Integer
Dim Bottom As Integer
End Structure
All the arguments during call of TrackPopupMenu has some values.
I tried different callingConvention but still getting the error.
I am not able to solve this. Does anyone know how to resolve this issue ?
Your declaration is wrong. The first argument is a handle to the menu, it must therefore be IntPtr. The last argument is a pointer to RECT. ByRef in VB.NET. Since it isn't actually used, you are better off declaring it ByVal IntPtr so you don't need the RECT declaration. Pass IntPtr.Zero in your call. The return value is Boolean, not Integer. Throw a Win32Exception if you get a False return. Fix:
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Friend Function TrackPopupMenu(ByVal hMenu As IntPtr, ByVal wFlags As Integer, _
ByVal x As Integer, ByVal y As Integer, ByVal nReserved As Integer, _
ByVal hWnd As IntPtr, ByVal ignored As IntPtr) As Boolean
End Function
There are some hints that you didn't get the menu right, hard to imagine how you came up with Long as the argument type. Do note that this function is already ably wrapped in .NET, having to pinvoke it should be extremely rare. TrackPopupMenu is already called by the .NET ContextMenu class. The ContextMenuStrip class gives a more modern version of it with better rendering options.

Declaring third party DLL dynamically in VB.NET

I'm facing a problem importing a DLL on different environments.
I have to check Windows platform and import the third-party DLL that will be placed in C:\Program Files\ (for 32-bit) or C:Program Files (x86)\ (for 64-bit).
Before the code was written like this:
Declare Function RDRCConnect Lib "c:\program files\TP-DLL\RDRCAP32.DLL" (ByVal lpszServerName As String, ByVal lNetConnType As Integer, ByVal lpszParam1 As String, ByVal lpszParam2 As String, ByVal lpszParam3 As String, ByRef lNetConn As Integer, ByRef lNetErr As Integer) As Integer
Declare Function RDRCDisconnect Lib "c:\program files\TP-DLL\RDRCAP32.DLL" (ByVal lNetConn As Integer, ByRef lNetErr As Integer) As Integer
...and I changed to use attributes:
Private Const CheminDLL As String = "C:\Program Files\TP-DLL\RDRCAP32.DLL"
<System.Runtime.InteropServices.DllImport(CheminDLL)>
Private Shared Function RDRCConnect(ByVal lpszServerName As String, ByVal lNetConnType As Integer, ByVal lpszParam1 As String, ByVal lpszParam2 As String, ByVal lpszParam3 As String, ByRef lNetConn As Integer, ByRef lNetErr As Integer) As Integer
End Function
How can I change the DLL path dinamically in this scenario, once the DLLImport expects a Constant as parameter?
There's no way to pass anything other than a constant into the attribute, since attributes, by definition are evaluated at compile-time, not at runtime. There may be better alternatives, but one option I can give you would be to create separate imports for each version:
<DllImport("C:\Program Files\TP-DLL\RDRCAP32.DLL", EntryPoint := "RDRCConnect")>
Private Shared Function RDRCConnect32(ByVal lpszServerName As String, ByVal lNetConnType As Integer, ByVal lpszParam1 As String, ByVal lpszParam2 As String, ByVal lpszParam3 As String, ByRef lNetConn As Integer, ByRef lNetErr As Integer) As Integer
End Function
<DllImport("C:\Program Files (x86)\TP-DLL\RDRCAP32.DLL", EntryPoint := "RDRCConnect")>
Private Shared Function RDRCConnect64(ByVal lpszServerName As String, ByVal lNetConnType As Integer, ByVal lpszParam1 As String, ByVal lpszParam2 As String, ByVal lpszParam3 As String, ByRef lNetConn As Integer, ByRef lNetErr As Integer) As Integer
End Function
Then you would need to choose which one to call appropriately each time you call the method.
I just stumbled upon this a couple of days ago when I had a similar issue to yours and it put me on the right path. Look for it here.
If the dlls are the same (same name and signatures) but just in different locations then you can p/invoke LoadLibrary explicitly with the full path of the dll as determined at runtime. As long as you do this before you invoke any of the exported APIs then it will use the one already loaded as long as the name of the dll in the import is the same.

" addressof " VB6 to VB.NET

I´m having some problem to convert my VB6 project to VB.NET
I don't understand how this "AddressOf" function should be in VB.NET
My VB6 code:
Declare Function MP4_ClientStart Lib "hikclient.dll" _
(pClientinfo As CLIENT_VIDEOINFO, ByVal abab As Long) As Long
Public Sub ReadDataCallBack(ByVal nPort As Long, pPacketBuffer As Byte, _
ByVal nPacketSize As Long)
If Not bSaved_DVS Then
bSaved_DVS = True
HW_OpenStream hChannelHandle, pPacketBuffer, nPacketSize
End If
HW_InputData hChannelHandle, pPacketBuffer, nPacketSize
End Sub
nn1 = MP4_ClientStart(clientinfo, AddressOf ReadDataCallBack)
You are probably seeing this error:
'AddressOf' expression cannot be
converted to 'Long' because 'Long' is
not a delegate type.
What you probably want to do is create a delegate then change the type of adab to that delegate type. Add this to the class:
Public Delegate Sub ReadDataCallBackDelegate(ByVal nPort As Long, _
ByVal pPacketBuffer As Byte, ByVal nPacketSize As Long)
Then change your P/Invoke declaration to:
Declare Function MP4_ClientStart Lib "hikclient.dll" (ByVal pClientinfo As _
CLIENT_VIDEOINFO, ByVal abab As ReadDataCallBackDelegate) As Long
Do not delete/change your ReadDataCallBack Sub, you still need that.
At that point he compiler should be happy. However, the point made by others is important. The length of Integers and Longs is different in VB6 than in VB.NET. So in .NET you need to use Integer anytime you used a Long in VB6.
Regarding callbacks in unmanaged code see if this similar post helps you.
Regarding your question - I don't think you need callback functions or the example you posted is not correct/complet - see the post indicated above and clarify your code sample.
I assume that the second parameter to MP4_ClientStart is supposed to be the address of a callback function. Likely the problem is that you've defined it here as a Long, which in VB6 is a 32-bit value, but in VB.NET is a 64-bit value. You'll probably have some success by changing your declaration to:
Declare Function MP4_ClientStart Lib "hikclient.dll" _
(pClientinfo As CLIENT_VIDEOINFO, ByVal abab As Integer) As Integer
Here is the VB.NET implementation:
Declare Function MP4_ClientStart Lib "hikclient.dll" (ByRef pClientinfo As _
CLIENT_VIDEOINFO, ByVal abab As ReadDataCallBackDelegate) As Integer
Public Delegate Sub ReadDataCallBackDelegate(ByVal nPort As Long, _
ByRef pPacketBuffer As Byte, ByVal nPacketSize As Long)
Public Sub ReadDataCallBack(ByVal nPort As Integer, ByRef pPacketBuffer As _
Byte, ByVal nPacketSize As Integer)
If Not bSaved_DVS Then
bSaved_DVS = True
HW_OpenStream(hChannelHandle, pPacketBuffer, nPacketSize)
End If
HW_InputData(hChannelHandle, pPacketBuffer, nPacketSize)
End Sub
MP4_ClientStart(clientinfo, AddressOf ReadDataCallBack)