(Talking about Visual Basic 6)
I was able to find how to convert Double into 8-bytes array, but not the viceversa.
Before I start to try to code it, is there some routine to do it (like the "CopyMemory" described in the linked question)? Can the "CopyMemory" be used in this case?
Use the same code as the answer you linked to but swap the source and destination around:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
ByRef Destination As Any, _
ByRef Source As Any, _
ByVal Length As Long)
Function BytesToDbl(ByRef Bytes() As byte) As Double
Dim D As Double
CopyMemory D, Bytes(0), LenB(D)
BytesToDbl = D
End Function
I've skipped any error checking for this example but you'll want to make sure that your byte array is actually 8 bytes long otherwise you'll get an access violation.
Note that this assumes the byte array was created using the linked to question. Floating point values from other sources may well be using a different binary representation which means this will not work.
Related
VC functions all use _stdcall
functions exported using .def file
(e.g. AliasFuncName = _FuncName#NumCallArgBytes)
Existing C DLL was revised to have some new call arguments
(revised function names, built to new dll name)
Functions with unrevised call arguments work when calling the new DLL
Functions with revised call arguments do not work when calling the new DLL
(all call arguments are garbage on entry)
Call arguments are several input doubles and a few return double*
Prototype.h call definition matches c source code definition
Visual Basic declarations to new DLL match in style those to the old DLL
(several ByVal double input args and a few ByRef return args)
Arguments look good in VB Debugger before calling VC debugger where they are garbage (e.g. 1.34867e-308, 3.49732e-88, etc.).
I would appreciate any thoughts on possible causes. I have been struggling with this for a few days. By the way, I don't choose to work in legacy code!
Below are the C header prototype, the .DEF definition and the VB declaration.
Header file definition:
LONG _stdcall SYSDll_FRoulSlideXa(
double ATest, double Hc, double Hivr,
double Eeq, double Rx, double Rk,
double L, double U, double SlRol,
double R, double Wlc, double Wpc,
double Mu, double MuOil, double Cor2AL,
double Fs, double Ft,
double *FRoul, double *FSlid);
.DEF file definition:
LIBRARY "SYSx32d10a"
DESCRIPTION 'SYSx Dlls'
EXPORTS
SYSDll_FRoulSlideXa = _SYSDll_FRoulSlideXa#144
VB6 declaration:
Declare Function SYSDll_FRoulSlideXa Lib "SYSX32D10A.DLL" ( _
ByVal ATest As Double, ByVal Hc As Double, ByVal Hivr As Double, _
ByVal Eeq As Double, ByVal rx As Double, ByVal Rk As Double, _
ByVal L As Double, ByVal U As Double, ByVal SlRol As Double, _
ByVal r As Double, ByVal Wlc As Double, ByVal Wpc As Double, _
ByVal Mu As Double, ByVal MuOil As Double, ByVal Cor2AL As Double, _
ByVal Fs As Double, ByVal Ft As Double, _
FRoul As Double, FSlid As Double)
Note: I have already tried explicit ByRef on the last two arguments instead of relying on default passing convention being ByRef.
You VB Declare doesn't include a return type for the function. Unless there's a DEFxxx statement that you don't show, that means VB expects a Variant. Because Variant functions return their value using a hidden parameter, the stack will be misaligned. That alone can cause what you're seeing.
The solution is to add the correct return type to the VB Declare.
Currently, my console application can return Integer values from the console application via the kernel32 function ExitProcess.
Public Declare Sub ExitProcess Lib "kernel32" (ByVal uExitCode As Long)
How do I return string values from the console application to the batch file?
I want to return string values like Successfully transformed 100 batches... etc.
On most platforms (and also in Windows) process exit codes are integer values, but you could write string data to the standard output stream by using the GetStdHandle and WriteFile functions.
Update As requested, I´ll serve you an example.
First, you´ll need to import some more Windows functions and define the required constants. In addition to the before-mentioned GetStdHandle and WriteFile methods, the AttachConsole and FreeConsole methods are also required.
Private Const ATTACH_PARENT_PROCESS As Long = -1
Private Declare Function AttachConsole Lib "Kernel32" ( _
ByVal dwProcessId As Long) As Long
Private Declare Function FreeConsole Lib "Kernel32" () As Long
Private Const STD_OUTPUT_HANDLE As Long = -11&
Private Declare Function GetStdHandle Lib "Kernel32" ( _
ByVal nStdHandle As Long) As Long
Private Declare Function WriteFile Lib "Kernel32" ( _
ByVal hFile As Long, _
ByVal lpBuffer As String, _
ByVal nNumberOfBytesToWrite As Long, _
ByRef lpNumberOfBytesWritten As Long, _
lpOverlapped As Any) As Long
In my sample project, I just added a Module and defined a Sub Main method - this serves as the entry point for the app. Please notice, that you don´t get any output from the app when running it from the VB6 IDE debugger. You´ll need to compile it, and run it from a terminal window (for instance cmd.exe).
The first thing to do is to attach to the console of the parent process (which is the console of the terminal window). Otherwise, the GetStdHandle method will return zero.
Dim handle As Long
AttachConsole (ATTACH_PARENT_PROCESS)
handle = GetStdHandle(STD_OUTPUT_HANDLE)
Once the console handle is obtained, the WriteFile method can be used to print text to the console.
Dim s As String
Dim numberOfBytesWritten As Long
s = "Hello World."
WriteFile handle, s, Len(s), numberOfBytesWritten, ByVal 0&
Before finally calling ExitProcess, the FreeConsole method is used to detach the process from the parent console.
In the new 64-bit version of Excel 2016 on OSX I obtained through update today, the conditional compilation doesn't seem to be followed when checking for function definitions that don't have PtrSafe defined (as would be the case for 32-bit platforms). In this example, we have different definitions of the same function for different platforms, and when Excel loads the add-in it dies and complains about the third definition not having a PtrSafe in the function declaration (but of course it doesn't because it is for a 32-bit platform).
Is there any way of making Excel not die when it hits this code in VBA? Or is this just a bug in 64-bit Excel 2016 on OSX? Seems like an obvious bug to me. Where do I report bugs in Excel?
#If Mac Then
' Even though the functions are exported with a leading underscore, Excel 2011 for Mac doesn't want the leading underscore as part of name
Private Declare PtrSafe Function get_global_param_string_private Lib "libCoolProp.dylib" Alias "get_global_param_string" (ByVal param As String, ByVal Output As String, ByVal n As Integer) As Long
#ElseIf Win64 Then
Private Declare PtrSafe Function get_global_param_string_private Lib "CoolProp_xls_x64.dll" Alias "get_global_param_string" (ByVal param As String, ByVal Output As String, ByVal n As Integer) As Long
#Else
Private Declare Function get_global_param_string_private Lib "CoolProp_xls_std.dll" Alias "_get_global_param_string#12" (ByVal param As String, ByVal Output As String, ByVal n As Integer) As Long
#End If
Unless the API function itself is different for 64 and 32 bit windows it suffices to use the VBA7 switch (which starts at Office 2010) for Windows:
#If Mac Then
' Even though the functions are exported with a leading underscore, Excel 2011 for Mac doesn't want the leading underscore as part of name
Private Declare PtrSafe Function get_global_param_string_private Lib "libCoolProp.dylib" Alias "get_global_param_string" (ByVal param As String, ByVal Output As String, ByVal n As Integer) As Long
#ElseIf VBA7 Then
Private Declare PtrSafe Function get_global_param_string_private Lib "CoolProp_xls_x64.dll" Alias "get_global_param_string" (ByVal param As String, ByVal Output As String, ByVal n As Integer) As Long
#Else
Private Declare Function get_global_param_string_private Lib "CoolProp_xls_std.dll" Alias "_get_global_param_string#12" (ByVal param As String, ByVal Output As String, ByVal n As Integer) As Long
#End If
I am trying to call the GetConsoleScreenBufferInfoEx function from a console application. If it matters, the application is a 32 bit application running on 64 bit Windows 7. The language is RealBasic.
I believe I have defined all the structures correctly, and the buffer output handle works for every other API function that is being called:
Declare Function GetConsoleScreenBufferInfoEx Lib "Kernel32" (cHandle As Integer, ByRef info As CONSOLE_SCREEN_BUFFER_INFOEX) As Boolean
Declare Function GetLastError Lib "Kernel32" () As Integer
Declare Function GetStdHandle Lib "Kernel32" (hIOStreamType As Integer) As Integer
Const STD_OUTPUT_HANDLE = -11
Dim stdHandle As Integer = GetStdHandle(STD_OUTPUT_HANDLE)
Dim err As Integer
Dim info As CONSOLE_SCREEN_BUFFER_INFOEX
If GetConsoleScreenBufferInfoEx(stdHandle, info) Then
Break
Else
err = GetLastError //Always 87, Invalid parameter
Break
End If
Structures:
Structure CONSOLE_SCREEN_BUFFER_INFOEX
cbSize As Integer
dwSize As COORD
CursorPosition As COORD
Attribute As UInt16
srWindow As SMALL_RECT
MaxWindowSize As COORD
PopupAttributes As UInt16
FullScreenSupported As Boolean
ColorTable(15) As UInt32
Structure COORD
X As UInt16
Y As UInt16
Structure SMALL_RECT
Left As UInt16
Top As UInt16
Right As UInt16
Bottom As UInt16
I've gone over this 20 times and nothing looks wrong to me. I've used the COORD and SMALL_RECT structures many times before, so I don't think I made any translation errors on them. The CONSOLE_SCREEN_BUFFER_INFOEX structure, however, is seeing its first use by me here, and I sense that the error lies somewhere in my translation of it.
You need to set the cbSize parameter of the CONSOLE_SCREEN_BUFFER_INFOEX before you send it in. GetConsoleScreenBufferInfoEx will check that it is the correct size and that's why it's returning an invalid parameter.
So before the call to GetConsoleScreenBufferInfoEx add:
info.cbSize = 96
Or better yet Real Basic does allow you to access the size of the structure:
info.cbSize = GetConsoleScreenBufferInfoEx.Size
Which should handle the calculation for you.
I have a simple DLL written with VC6 with one function:
__declspec(dllexport) int myfunc(long a, unsigned char *b, unsigned char *c, unsigned char *d, unsigned char *e)
And im calling it from vb6 using:
Declare Function myfunc Lib "mylib.dll" (ByVal a As Long, ByVal b As String, ByVal c As String, ByVal d As String, ByVal e As String) As Long
....
dim a as long
dim b as string
dim c as string
dim d as string
dim e as string
dim r as long
r=myfunc(a,b,c,d,e)
Im getting "bad dll calling convention" error but I cant figure out why. Any ideas?
Generally speaking, 'bad DLL...' means what it says. VB6 requires the _stdcall convention (like the Win API) for any external functions it calls.
Try adding __stdcall to the C function prototype and see what happens.
Check out the Universal DLL function caller, by Paul Caton:
http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=70195&lngWId=1
It will allow you to call pretty much any type of function from VB6.