Unhandled AccessViolationException Error calling a DLL into VB.Net - c++

I'm trying to call a C++-compiled DLL from VB.net and I'm running into some problems. Seems like there's an obvious solution, but I can't figure it out.
Here is the function declaration in C++:
MyFunction(int trailingaveragesize, double sigmasize, int myflag, int sizeSeries, double *Xdata, double *Ydata, int sizeinputparameter, int *averagePairs, double *PositionsSize, double *PnLSize)
Here is how I'm calling it in VB.Net:
Call MyFunction(200, 1, 1, 230, a_PriceSeries(0), a_PriceSeries(0), 1, a_Averages(0), a_PositionSeries(0), a_PnLs(0))
The maximum size of the input matrices are defined by sizeSeries (230), and the size of all my input matrices are 10000 (just so I won't accidentally overflow), yet still i'm getting an unhandled AccessViolationException error
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
My question is - If I'm not exceeding the bounds of my matrices, what other reasons would throw this error? Is it because I'm only passing the first entry in my matrices ByReference then it's trying to access other elements of that matrix? If so, how would I fix that?
EDIT:
Here's how I'm declaring it in VB
Declare Function MyFunction Lib "C:\Dev\asdf.dll" (ByVal trailingaveragesize As Long, ByVal sigmasize As Double, ByVal myflag As Long, ByVal sizeSeries As Long, ByRef Xdata As Double, ByRef Ydata As Double, ByVal sizeinputparameter As Long, ByRef averagePairs As Long, ByRef PositionsSize As Double, ByRef PnLSize As Double) As Double

Declare Function MyFunction Lib "C:\Dev\asdf.dll" (ByVal trailingaveragesize As Long, _
ByVal sigmasize As Double, ByVal myflag As Long, ByVal sizeSeries As Long,
ByRef Xdata As Double, ByRef Ydata As Double, ByVal sizeinputparameter As Long,
ByRef averagePairs As Long, ByRef PositionsSize As Double, ByRef PnLSize As Double) As Double
The declaration is simply wrong, this resembles a vb6 declaration. An int in C code is an Integer in vb.net, not a Long. The Xdata and Ydata are highly likely to be arrays, not a byref double. Declare them as ByVal Double(). The other byref args are harder to guess.

p-invoke is platform invoke, and is how you call into native APIs with .NET. Your declaration is not currently setup to pass arrays, and it should not be done ByRef.
Try changing the ByRef to ByVal for your array variables and declare them with the () to signify an array.

The error is that while I was declaring a variable as type long but it should be of type integer. The variabe rc was the one wrongly declared:
Here the code of my form Form1 (it has one Button and one TextBox):
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Here the code for Button.Click (it initialize some variables used to call a function in a dll):
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim arg1 As String
Dim arg2 As String
Dim ret As String
Here is the variable rc that I declared as long. It should be integer
Dim rc As long
ret = "123456789012345678901234567890123"
arg1 = "123456"
arg2 = "1234567890123456"
rc = myFunction(arg1, arg2, ret)
If (rc = 0) Then
TextBox1.Text = ret
Else
TextBox1.Text = "Result not OK"
End If
End Sub
End Class
myFunction was declared as integer in myDll:
extern __declspec(dllexport) int __stdcall myFunction(unsigned char * pszArg1, unsigned char * pszArg2, char * pszReturn)
This is how I declared MyFunction in my Visual Basic Express 2010:
Module Module1
Public Declare Function myFunction Lib "C:\Users\me\MyProjects\myDll\bin\Debug\myDll.dll" (ByVal Arg1 As String, ByVal Arg2 As String, ByVal ret As String) As Integer
End Module

Related

Excel-VBA with C++ DLL sometimes crashes

I made really simple c++ dll with only one function:
int DLL_EXPORT __stdcall foo(double *source){return 0;}
and I'm trying to use it like that:
Option Explicit
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
Private Declare PtrSafe Function FreeLibrary Lib "kernel32" (ByVal hLibModule As LongPtr) As Long
Private Declare PtrSafe Function foo Lib "MyLibrary.dll" (ByRef arr As Double) As Long
Sub test_foo(n As Long)
Dim i As Long
Dim library_address As LongPtr
Dim library_path As String
library_path = "global_path\MyLibrary.dll"
library_address = LoadLibrary(library_path)
Dim arr() As Double
ReDim arr(1 To n) As Double
For i = 1 To n
arr(i) = CDbl(Cells(i, 1).Value)
Next
foo arr(1)
Do Until FreeLibrary(library_address) = 0
Loop
End Sub
and it usually works, but sometimes it crashes (Excel dies).
Faulting application name: EXCEL.EXE, version: 16.0.8625.2139, time stamp: 0x5a162a41
Faulting module name: MyLibrary.dll_unloaded, version: 0.0.0.0, time stamp: 0x000000e2
Exception code: 0xc0000005
Fault offset: 0x00001230
Faulting process id: 0x1828
I've tested on Excel 2016 on Windows 10 and Excel 2013 on Windows 8
Please tell me what is wrong? Do you have any example of non-crashing usage of C++ DLL working on arrays?
WORKAROUND:
Replace
Do Until FreeLibrary(library_address) = 0
Loop
with
FreeLibrary library_address
I've put that in the loop because sometimes FreeLibrary doesn't work, but i don't care anymore. Related question here
Integer in VBA is from -32768 to 32767. In C++ it is a way bigger, equivalent to the VBA Long. Thus, try declaring like this:
Private Declare PtrSafe Function foo Lib "MyLibrary.dll" (ByRef arr As Double) As Long
Let me show what I did so far, which worked for me (if this was the way you wanted it). Anyhow, I have changed a few things, it will be better to use some text comparer to see a bit).
I have used this article to build the dll library (disclaimer - it is my own).
The cpp and the def:
int __stdcall SimpleSlowMath(double *source)
{
return 0;
}
The *.def looks like this:
LIBRARY "SomeLibrary"
EXPORTS
SimpleSlowMath
The VBA:
Option Explicit
Public Const myPathDll = "C:\Users\your-own-path\Debug\vityata051217.dll"
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" _
(ByVal lpLibFileName As String) As Long
Private Declare PtrSafe Function FreeLibrary Lib "kernel32" _
(ByVal hLibModule As LongPtr) As Long
Private Declare PtrSafe Function SimpleSlowMath Lib "vityata051217.dll" _
(ByRef arr() As Double) As Long
Sub Try(n As Long)
Dim i As Long
Dim library_address As Long
Dim library_path As String
library_path = myPathDll
library_address = LoadLibrary(library_path)
Dim arr() As Double
ReDim arr(1 To n) As Double
For i = 1 To n
arr(i) = CDbl(Cells(i, 1).Value)
Next
Debug.Print SimpleSlowMath(arr)
End Sub
Public Sub TestMe()
Dim n As Long
For n = 1 To 50
Try n
Debug.Print n
Next n
End Sub
As you see, the difference is that I declare the array with () here - ByRef arr() As Double, but there are some other as well. Give it a try, for me it was working for 2000 samples.

Invalid procedure call or argument when passing parameters from VBA to WIndows DLL

I have 64 bit windows 10 with MS Office 64 bit. I am trying to get the VBA for PowerPoint to load and execute a function in a self-written 64 bit windows DLL.
The problem I am having is that I get an exception - which I think is due to stack handling.
The dll contains (exports done in def file):
void _stdcall jastrNR(LPCWSTR strg1, LPCWSTR strg2, LPCWSTR strg3, LPCWSTR strg4)
The VBA contains:
Private Declare PtrSafe Sub jastrNR Lib "<DLL path>" (ByVal arg1 As LongPtr, ByVal arg2 As LongPtr, ByVal arg3 As LongPtr, ByVal arg4 As LongPtr)
Sub Play1()
Dim s1 As String
Dim p1 As LongPtr
Dim y As Long
y = 0
MsgBox y
s1 = "123abc"
p1 = StrPtr(s1)
jastrNR p1, p1, p1, p1
p4 = StrPtr(s4)
MsgBox y *** here I get exception or "invalid procedure call or argument" message
End Sub
If I print out the test string in the DLL, I do see correct data, ie 123abc
If the parameters are long, ints, there is no problem.
Also there is no problem if I make jastrNR return a long.
I have also tried to match the parameters in the C++:
void _stdcall jastrNR(LONG_PTR strg1, LONG_PTR strg2, LONG_PTR strg3, LONG_PTR strg4)
I get the same exceptions.
In Visual Studio 2017, I have set (under C++ settings) the calling convention to be __stdcall (/Gz).
I have noticed in the linker map output:
?jastrNR##YAX_J000#Z (void __cdecl jastrNR(__int64,__int64,__int64,__int64))
exported name: jastrNR
The __cdecl concerns me as I thought I should have seen __stdcall
Any help gratefully received.

Wrong DLL calling convention

I'm trying to call a C++ COM DLL from within VB6.
The C++ code is:
STDMETHODIMP CSonic::sonicChangeShortSpeed(
SHORT* samples,
LONG *numSamples,
FLOAT speed,
FLOAT pitch,
FLOAT rate,
FLOAT volume,
LONG useChordPitch,
LONG sampleRate,
LONG numChannels
)
I call it from VB6 like this:
Private Declare Function sonicChangeShortSpeed Lib "SonicLIB.dll" Alias "#1" (
ByRef samples As Integer,
ByRef numSamples As Long,
ByVal speed As Double,
ByVal pitch As Double,
ByVal rate As Double,
ByVal volume As Double,
ByVal useChordPitch As Long,
ByVal sampleRate As Long,
ByVal numChannels As Long)
As Long
In my code, I use:
Dim nIntegers() As Integer
ReDim nIntegers(2047)
Dim lSamples As Long
Dim dblSpeed As Double
Dim dblPitch As Double
Dim dblRate As Double
Dim dblVol As Double
Dim lUseChordPitch As Long
Dim lSampleRate As Long
Dim lNumChannels As Long
lSamples = 2048
dblSpeed = 0.5
dblPitch = 0
dblRate = 1
dblVol = 1
lUseChordPitch = 1
lSampleRate = 48000
lNumChannels = 1
Dim lRet As Long
lRet = sonicChangeShortSpeed(nIntegers(0), lSamples, dblSpeed, dblPitch, dblRate, dblVol, lUseChordPitch, lSampleRate, lNumChannels)
The last line produces the "Wrong calling convention" error.
Does anybody see my mistake?
Thank you!
If this is a COM DLL, you need to add it to your VB6 project as a reference, and then you can access the classes and other COM definitions contained in the DLL.
Alternately you could just register the DLL without adding it as a reference and then use 'late binding' with CreateObject() calls.
But if you are not sure if it is a COM DLL, you could check by:
Try to register it on the command like using regsvr32.exe (may need to be admin). If this reports success, that means it was a successfully registered COM DLL.
Drag and drop it into the program Oleview which comes with Visual Studio. If the DLL is COM it will normally have a type library which Oleview will display. If it is not COM, this will produce an error.
If it is NOT a COM DLL then I do not think your approach will necessarily work. You are trying to call a class method as though it were a normal 'C' style function. Perhaps that would work for a static C++ method -- but that does not appear to be the case here.
The traditional import/export table of a DLL knows nothing about classes. You may need to write a C++ wrapped function around your class object which can then be exported and used in VB6. The wrapper would have to handle object creation, destruction, etc.

C++ code translation in VB6

I've a C dll and I want to use it in vb6, here is the C syntax:
#ifndef _MSC_VER
typedef long long INT64T;
#else
typedef __int64 INT64T;
#endif
int Init(int opts, void *key, INT64T offs);
I've converted it to VB6:
Public Declare Function Init Lib "x.dll" (ByVal opts As Integer, ByVal key As Long, ByVal offs As Currency)
and call it:
Init 0, 0, 0
some part of the function is executed and the I've got this error:
Bad DLL calling convention
would you please kindly let me know what is the problem? The dll is from third-party dll so I don't know anything more about it,
For 64 bit you should use:
Type ULARGE_INTEGER
LowPart As Long
HighPart As Long
End Type
and your declaration should be like this:
Public Declare Function Init Lib "x.dll" (ByVal opts As Long, ByVal key As Long, ByVal offs As ULARGE_INTEGER)
Public Declare Sub CopyMemory Lib "kernel32" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)
Now to the conversions:
After you get the ULARGE_INTEGER you can translate it to Currency like this:
' Copy int64 into the Currency data type
Dim curr as Currency
Dim int64 as ULARGE_INTEGER
' Copy ULARGE_INTEGER into the Currency data type
CopyMemory curr, int64, 8
' Multiply by 10000 to move Visual Basic decimal point to the end of the actual number
curr = curr * 10000
to traverse back to ULARGE_INTEGER from Currency:
Dim curr as Currency
Dim int64 as ULARGE_INTEGER
' Devide by 10000 to move Visual Basic decimal point to original position
curr = curr * 0.00001
' Copy Currency into the ULARGE_INTEGER data type
CopyMemory int64, curr, 8

Calling a DLL from VBA

I need to call a DLL function from Access using VBA. The prototype for the DLL function is
int __stdcall myFunction(const char* data,
int rows, int cols, int sl, int fullsize,
double aspect_ratio,
double y,
void** ppResult);
The one I declared in Access:
Private Declare Function DllImport_myFunction _
Lib "myFunctionDLL.dll" _
Alias "myFunction" _
(ByVal data As String, _
ByVal rows As Long, _
ByVal cols As Long, _
ByVal sl As Long, _
ByVal fullsize As Long, _
ByVal aspectRatio As Double, _
ByVal y As Double, _
ByRef handle As Long)
When I try to call from Access, Access crashed with an access violation. I placed a breakpoint in the first statement of the DLL function, but it was not hit.
Is the declaration incorrect?
You are missing the return
Private Declare Function DllImport_myFunction Lib "myFunctionDLL.dll" Alias "myFunction" _
(ByVal data As String, _
ByVal rows As Long, _
ByVal cols As Long, _
ByVal sl As Long, _
ByVal fullsize As Long, _
ByVal aspectRatio As Double, _
ByVal y As Double, _
ByRef handle As Long
) As Long
and probably need to add extern "C" to avoid mangling.