I'm trying to make a C++/CLI dll to wrap a managed assembly so it can be called from VBA inside Excel (I specifically want to avoid using COM). It shall only support 64 bit.
I have based my test code on the dotnet cpp-cli sample and added four additional native entry points:
extern "C" MIXEDLIBRARY_API int __stdcall CalcIntByVal(const int value)
{
return 42 + value;
}
extern "C" MIXEDLIBRARY_API double __stdcall CalcDoubleByVal(const double value)
{
return 3.14 + value;
}
extern "C" MIXEDLIBRARY_API int __stdcall CalcIntByRef(const int* value)
{
return 42 + *value;
}
extern "C" MIXEDLIBRARY_API double __stdcall CalcDoubleByRef(const double* value)
{
return 3.14 + *value;
}
I made a small Excel VBA sample that I debug from Visual Studio (use excel as debugging command or attach to process).
It first calls the two 'ByRef' methods then the 'ByVal' methods. Here is the VBA code:
Option Explicit
Private Declare PtrSafe Function SetDllDirectoryW Lib "kernel32" (ByVal lpPathName As LongPtr) As Long
Private Declare PtrSafe Function CalcIntByVal Lib "MixedLibrary.dll" (ByVal value As Long) As Long
Private Declare PtrSafe Function CalcDoubleByVal Lib "MixedLibrary.dll" (ByVal value As Double) As Double
Private Declare PtrSafe Function CalcIntByRef Lib "MixedLibrary.dll" (ByRef value As Long) As Long
Private Declare PtrSafe Function CalcDoubleByRef Lib "MixedLibrary.dll" (ByRef value As Double) As Double
Private Sub CommandRun_Click()
Dim dllPath As String
dllPath = "MixedLibrary.dll" ' <- native x64 shim
Dim dllDir As String
dllDir = "C:\temp\\cpp-cli\bin\Debug\x64" ' <- folder with shim *and* its deps
' 1) Add dir so dependencies are found
Call SetDllDirectoryW(StrPtr(dllDir))
Dim myNumber As Long
Dim dnum As Double
dnum = 456#
myNumber = 123
' ByRef calls NOT causing exception
' Call the C++ method with int
myNumber = CalcIntByRef(myNumber)
' Call double
dnum = CalcDoubleByRef(dnum)
' ByVal call causing exception
myNumber = CalcIntByVal(myNumber)
dnum = CalcDoubleByVal(dnum)
End Sub
No issue is seen with the first two XXXByRef calls, but with the XXXByVal calls an access violation exception is seen on entry to both methods.
Exception seen on call to CalcDoubleByVal
The value of the argument is also not transferred correctly for the CalcDoubleByVal method.
The exception seems to indicate that the stack or memory has been corrupted, but I cannot find the root cause.
A sample solution can be found here.
https://drive.google.com/file/d/1IyqTzAHGkzZAkBV0JyWlDp8cjnWL2EFP/view?usp=sharing
When debugging the top of the call stack at the time of exception is:
kernel32.dll!IsBadStringPtrA() Unknown
VBE7.DLL!MereLogWinApi(struct serDllTemplate *,unsigned short,void *) Unknown
VBE7.DLL!DllFunctionCall() Unknown
So it seems to be connected with some logging inside VBE7.dll just prior to making the dll call.
From the dissassembly:
sub rsp,18h
test rdx,rdx
je IsBadStringPtrA+3Dh (07FFBDBA10B0Dh)
test rcx,rcx
je IsBadStringPtrA+45h (07FFBDBA10B15h)
mov qword ptr [rsp],rcx
lea r8,[rdx-1]
lea r8,[r8+rcx]
mov al,byte ptr [rcx]
test al,al
It fails in the last line, because what seem like it is interpreting the argument as a string.
ByValwork for all standard types particularlychar,intanddouble? You sayByReffails on entry to the function? What I was going to suggest was declare an array in VBA and pass two consecutive elements asByRefto a test function and return the difference of their addresses. But that won't work if the code is failing on entry.