I'm experimenting with changing a vb 2015 Integer into a vb 2015 UInteger via a direct bit-by-bit transfer in ML32. From vb2015, I call a vc++ 2015 function in a dll which uses the inline assembler to effect the change.
By doing this, I can take a negative Integer such as -633593090 = &HDA3C22FE = 11011010001111000010001011111110b
And change it to a UInteger = 11011010001111000010001011111110b = &HDA3C22FE = 3661374206
As opposed to something like the seemingly simple "uTest1 = CUInt(iTest1)" which throws a System.OverflowException for iTest1 negative.
I have code which works properly, but I'm interested in the possibility of a small improvement.
In vb 2015, I declare the access to the vc++ 2015 dll like this:
' vb Integer --> c++ signed long --> c++ unsigned long --> vb UInteger -- Works!
' by direct copy of bits through x86 ML32
' 4 bytes = 32 bits
<DllImport("StringTest.dll", EntryPoint:="bitConvert", SetLastError:=True, CharSet:=CharSet.Ansi, ExactSpelling:=True, CallingConvention:=CallingConvention.Cdecl)>
Private Shared Function bitConvertTester(ByRef varSInt As Integer, ByRef varUInt As UInteger) As Integer
' Must be "Shared"
' Must be "ByRef"
' Do not try varSInt As "Signed Integer" or varUInt As "Unsigned Integer" --> Syntax Error
' Leave the body of the function empty
End Function
And I use this vb 2015 code to actually call the vc++ 2015 function in the dll:
' Third Method - Partial - Works!
Dim pCTest As Color
Dim iTest As Integer
Dim uTest As UInteger
Dim returnCode As Integer
pCTest = Color.FromArgb(&HDA, &H3C, &H22, &HFE)
iTest = pCTest.ToArgb
System.Diagnostics.Debug.Write(iTest.ToString + vbCrLf)
System.Diagnostics.Debug.Write(Hex(iTest) + vbCrLf)
' vb Integer --> c++ signed long --> c++ unsigned long --> vb UInteger
' by direct copy of bits through x86 ML32
' 4 bytes = 32 bits
' Note: Function prototype is "ByRef", but this MUST
' NOT go in this test call (causes a syntax error).
returnCode = bitConvertTester(iTest, uTest)
System.Diagnostics.Debug.Write(uTest.ToString + vbCrLf)
System.Diagnostics.Debug.Write(Hex(uTest) + vbCrLf)
And here is the vc++ 2015 dll function itself:
#define EXPORT_VB extern "C" __declspec(dllexport)
// vb Integer --> c++ signed long --> c++ unsigned long --> vb UInteger -- Works!
// by direct copy of bits through x86 ML32
// 4 bytes = 32 bits
EXPORT_VB long __cdecl bitConvert(signed long *varSInt, unsigned long *varUInt)
{
// Intermediate variables are required for ML32 access.
signed long varSInt1;
unsigned long varUInt1;
varSInt1 = *varSInt;
__asm
{
mov EAX, varSInt1
mov varUInt1, EAX
}
*varUInt = varUInt1;
return 1; // Success Code
}
I call the vc++ 2015 dll function from vb 2015 with the variables ByRef so that the dll function can use pointers to change the vb 2015 variables as needed via *varSInt and *varUInt.
My question is: In the vc++ __asm block, is there any way for the ML32 code to access *varSInt and *varUInt directly, without going through the varSInt1 and varUInt1 intermediate variables?
BTW, the vb 2015 code uses:
Imports System
Imports System.IO
Imports System.Text
Imports System.Runtime.InteropServices
And the vc++ 2015 code uses:
#include "stdafx.h"
#include <stdexcept>
#include <string.h>
(One thing I find frustrating with Microsoft documentation is the failure to include a list of the "Imports" or "#include" statements required for use of the feature the documentation is describing).
I realise that you said you are just doing this as an exercise, but there is a relatively easy way to get the result you want in VB (or C# for that matter).
Function ToUInt(value As Integer) As UInteger
Return CUInt(If(value < 0, UInteger.MaxValue + value + 1, value))
End Function
Related
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.
this is an odd behaviour which I encouter.
I have a Visual Basic Interface for the User.
Declare PtrSafe Sub getPEC _
Lib "C:\Users\...somePath...\OPunit0011PUMP.dll" _
(ByVal typeOfPump As Integer, _
ByVal outflow As Double, _
ByRef pec As Double, _
ByRef RV As Integer)
The user specifies a pump by the integer typeOfPump. I pass this parameter as ByVal typeOfPump into my C++ DLL.
Here according to which pump it is some predeclared parameters a,...g are initialized using switch case
extern "C"
{
double PEC_backUp;
void __stdcall getPEC(int typeOfPump, double outflow, double &PEC, int &RV)
{
//polynomal trendline for different pumps
//y = a x^6 + b x^5 + c x^4 + d x^3 + e x^2 + f x^1 + g x^0
if (typeOfPump < 1)
{
RV = -1;
return;
}
double a, b, c, d, e, f, g;
#pragma region switch case
switch (typeOfPump)
{
//150 bar
case 1:
a = 1.1186E-08;
b = -1.49172E-05;
//...
break;
case 2:
//...
}
My problem is that switch case does NOT work. My default value is set to nine, but also every other case does NOT work. It simply neglects the switch case code.
Note also: The same odd behaviour can be seen in the If condition:
if (typeOfPump > 1)
{
RV = -1;
return;
}
Despite the fact that typeOfPump is assigned to NINE which is obviously bigger than one my function getPEC does not return at this point. On the other hand if I write
if (typeOfPump < 1)
{
RV = -1;
return;
}
my function will return here. I then assigned the value of typeOfPump to RV to monitor it in VBA and RV was set to nine.
Moreover, to make things even stranger it automatically changes the value of pec to 7.00000000005821 (using watch function of VBA) when it returns with RV = -1.
I guess my parameter are somehow not compatible for operations in my DLL. Did anyone see this before and how can I fix it?
Thank you in advance!
EDIT: I can do operations like
RV = typeOfPump * (int)outflow;
and obtain correct values. However, pec still shows some change in its value.
SCD EDIT: I have 64bit, Excel is 32bit, I'm compiling with x86.
I wrote a similar program on another computer 64bit, Excel 64bit, compiling with x64. There it worked!
3rd EDIT: integer of value 9 in VBA results in -65526 in C++ environment, given size of integer in my C++ environment is 4byte. Assuming range of 16bit variable for integer is −32,768 to 32,767. Doubling 32,767 and subtracting 9 leads to 65525.
In general if you're not sure why your code isn't working, print out the values that you are sure of, and see if they are actually what you think they are. Especially if mixing 32bit and 64 bit code. There are all kinds of compiler options which will affect the data format when it's used.
Write your code as a standalone exe, make sure that it works; write the dll with handshaking as simply as possible, make sure that works, and then make your exe a dll. You're trying to do too much in one step.
I have the following c++ code :
#include <OAIdl.h> // for VARIANT, BSTR etc
__declspec(dllexport) void __stdcall updatevar(VARIANT * x)
{
double nb = 3.0;
++nb;
}
with a function doing (almost) nothing (to avoid warnings)
and a .def file :
LIBRARY tmp0
EXPORTS
updatevar #1LIBRARY
I compile this with visual studio 2013 into a dll that I reference as follows in excel-2013's VBA :
Declare Sub updatevar Lib "C:\path\to\thedll.dll" (ByRef x As Variant)
and that I use like this :
Public Sub DoIt()
Dim r As Range
Set r = ActiveWorkbook.Sheets("Sheet1").Range("B2:C4")
Dim var As Variant
var = r.Value
Call updatevar(var)
End Sub
where the 6-cells excel range B2:C4 contains strings, dates, doubles and ints/longs.
I put a breakpoint in c++ code to inspect the variant pointed to that I receive, as remarked that the type of its 6 elements is always rightly resolved : dates go to a vt (variant type VT_DATE) equal to 7, doubles to a vt (variant type VT_R8) equal to 5, strings go to a vt (variant type VT_BSTR) of 8, except for ints/longs, that are mapped to VT_R8 and treated as doubles.
At the beginning I thought it was a c++ problem but, already inspecting the range r in the VBA code, and its Value2 field, showed to me that all ints/longs were treated in VBA as Variant/Double and not Variant/Long, and I have no idea why this is happening.
Note. I put c++ and dll tags as the people interested in these tags may also help given the context involving exchanging VARIANT's between c++ and VBA.
Remark. "Downcasting" from double to int is not an option, especially as VARIANT is supposed to know about ints/longs (VT_I4 = 2 and VT_I4 = 3 do exist.)
I have VBA program running on Excel 64-bit calling some DLL functions (C++). The problem is (apparently) it can't pass pointers to C++ program. The program works with Excel 32-bit.The operating system is Windows 8. For Windows 7 both 32-bit and 64-bit versions of Excel are working well.
C++ :
double test(long* v, long i)
{
if (v == NULL)
return -88;
else
return *((long*)v);
}
VBA:
Private Declare PtrSafe Function hamid_test Lib "...\CVode.dll" (ByVal v As LongPtr, ByVal i As Long) As Double
Dim x As LongLong
Dim z As Double
z = test(x, 1)
It returns -88.
Dim x As LongPtr
Dim z As Double
z = test(x, CLng(1))
LongPtr evaluates to Long in 32-Bit environments and to LongLong in 64-Bit environments so defining x as such should work.
Addtionally, CLng will ensure 1 is evaluated as a Long type. If this is not added VBA will assume you mean Integer
I´m creating a .dll library in C/C++ for VBA. It will contain functions for communication via RS232 serial port and data will be processed in Excel. Everything works fine but I´m confused of strange behavior of VBA that works under Excel. I have 2 functions. One for writing to port one for reading. When I´m sending a port number e.g. 3 from VBA to one of them, doesn´t matter which one and print it exactly after it was received by function it shows decimal value of 3 what is correct. But when I send exactly the same variable that consists number 3 to second one, function receive 51 what is a decimal value of "3" char. So at first VBA send integer then it changes somehow and send decimal value of "3" char. There is no code before printing received variable in my functions that can change value.
Here is simplified code of my functions just to show.
int __stdcall PortRead(short int & Port){
printf("%d %c\n",Port,Port);
return 0;
}
int __stdcall PortWrite(short int & Port, BSTR & Message){
printf("%d %c\n",Port,Port);
return 0;
}
Here is VBA code:
Declare Function PortRead Lib "rs232_r.dll" (ByRef x As Integer) As Integer
Declare Function PortWrite Lib "rs232_w.dll" (ByRef x As Integer, ByRef y As String) As Integer
Dim Message As String
Dim PortNumber As Integer
Sub Example()
PortNumber = 3
Message = ":trac:data?"
aa = PortWrite(PortNumber, Message)
Debug.Print aa
xx = PortRead(PortNumber)
Debug.Print xx
End Sub
As I said, passed values will be different when I'm sending one variable to 2 functions but when I change it like the next example both functions will receive the same correct value.
Declare Function PortRead Lib "rs232_r.dll" (ByRef x As Integer) As Integer
Declare Function PortWrite Lib "rs232_w.dll" (ByRef x As Integer, ByRef y As String) As Integer
Dim Message As String
Dim PortNumber1 As Integer
Dim PortNumber2 As Integer
Dim Number As Integer
Sub Example()
Number = 3
PortNumber1 = Number
PortNumber2 = Number
Message = ":trac:data?"
aa = PortWrite(PortNumber1, Message)
Debug.Print aa
xx = PortRead(PortNumber2)
Debug.Print xx
End Sub
I apology if this question has been already asked or if it is question for programmers from kindergarten but I am very curious. Thanks.
In VBA try changing ByRef to ByVal
The code posted maps "integer = int" and "integer = short int". Which is it? (Ar you compiling 32bit cpp or 64bit cpp?).
Excel will repair the stack if your declarations are invalid, and Excel will not crash, so you should not depend on crashing Excel to find invalid declarations.
When you call a C++ function from VBA ByRef, you might expect to receive a reference in the C++ function, like
short int & Port
But ByRef in VBA means "ByPointer" in C++.
So I found an issue. As you suggested I took closer look on parameters sent to function ByRef. After few experiments I discovered my mistake. I sent a number to function by reference that means I sent only address of memory not a value. Then my first function made few calculations because of conversion to char and creating string and saved a new changed value because of calculations to previous memory address. So when I was sending variable to second function also by reference, the value saved on memory address was already changed by my first function. So I was rewriting value of variable by myself. I should apology to Microsoft for my rude words that I said about its products while I was solving this problem. :) Thanks mates