Link to home
Start Free TrialLog in
Avatar of RWlodarczyk
RWlodarczyk

asked on

Passing an array from Visual Basic to a Delphi DLL

I am banking all of my points on asking this question... I really need to solve this problem!

I have written a DLL in Delphi 4.0. It compile successfully and I am able to call it and acces the procedure that I need in it from within VB.

Here is the code for the declaration of the procedure in Delphi:

TYPE
   myarray = array of double;

PROCEDURE myprocedure (rob : myarray;
                       width, height : integer;
                       ndimen, isign : integer); stdcall;

I pass it an array from within VB, but the as soon as I exit out of the DLL the changes are not saved to the array. (I have tried putting "VAR" in front of rob... but it did not solve the problem)

This is my DLL Declare in VB:

Declare Sub myprocedure lib "delphidll.dll" (ByRef junk as double,
                                             ByVal wid as integer,
                                             ByVal hgt as integer,
                                             ByVal ndim as integer,
                                             ByVal isign as integer)

I call the DLL as follows from VB:

    ReDim sindata(1 To 256 * CLng(256)) As Double
    i = 1
    While (i < 256 * CLng(256))
        sindata(i + 1) = Sin(i)
        sindata(i) = 0
        i = i + 2
    Wend

myprocedure sindata(1, 1), 128, 256, 2, 1

I can debug the DLL from within Delphi once the VB app is compiled into an EXE. I set breaks in the DLL, and I see that the DLL is populated correctly, and while the dll is running, the values in the array are changed, but the thing is this... once the dll is finished, then all of the changes are not retained... meaning once back in VB code, the array has the ORIGINAL values in it.

Can someone help me with this problem that I've got? If you would like more details about the problem email me: rwlodarc@ic.sunysb.edu and I'll be glad to send you the code for both the Delphi DLL and the VB App.

Thanks in advance!
Avatar of tomook
tomook

What is the definition of the array "Complex"? If it is not declared as an array of "Long", you are getting a temporary copy, much like in C++ if you pass an object with a type conversion operator, it creates a temporary object.
Avatar of RWlodarczyk

ASKER

Edited text of question
Adjusted points to 185
Adjusted points to 195
Sometimes you can fool VB by changing the function declaration to As Any.
Declare Sub myprocedure lib "delphidll.dll" (ByRef junk as Any,
                                             ByVal wid as integer,
                                             ByVal hgt as integer,
                                             ByVal ndim as integer,
                                             ByVal isign as integer)

If this does not work, please try using a Double variable instead of an array element. I am curious if it would work.
it also looks like in your call to the DLL, you are passing a value of the array instead of the array instead:

ReDim sindata(1 To 256 * CLng(256)) As Double
    i = 1
    While (i < 256 * CLng(256))
        sindata(i + 1) = Sin(i)
        sindata(i) = 0
        i = i + 2
    Wend


'myprocedure sindata(1, 1), 128, 256, 2, 1
myprocedure sindata(), 128, 256, 2, 1

Adjusted points to 200
The reason why anthonyc's answer is wrong is BAD PROGRAMMING! This array... sindata() may be in excess of 5 MB! You don't want to pass the entire array! You only want to pass the first element of the array, then, knowing the bounds of the array, you can locate any element in the array!
so you are relying on VB to place all array elements next to one another?  now that's bad programming.

VB when using redim preserve... you are screwed.
I'm not using redim preserve...


but if you are writing a general purpose dll.......nevermind
Did you try "As Any"?
I will wade in with a possibility.

In your code you define the delphi type as
TYPE
   myarray = array of double;

an un bounded array definition, then .....
The procedure definition.....
PROCEDURE myprocedure (rob : myarray;
                       width, height : integer;
                       ndimen, isign : integer); stdcall;

If you compare it with the VB definition
Declare Sub myprocedure lib "delphidll.dll" (
          ByRef junk as double,
          ByVal wid as integer,
          ByVal hgt as integer,
          ByVal ndim as integer,
          ByVal isign as integer)

You are giving a reference to a double variable, rather than a double array.  You might try changing the first parameter to
ByRef junk() as double, also unfortunately languages have a tendancy to have different views on the definition of a type.  Hopefully the delphi type DOUBLE and the VB type DOUBLE are the same, especially in storage terms ( VB double is 8 bytes,  what is delphi?).  

Again verify the size of the delphi integer ( 2 or 4 bytes ) as the VB integer is only 2 (+/- 32768 ish ).  

From a long ago memory, pascal type languages ten to set up the stack in a weird way for un bounded arrays, often by putting the bounds of the array at the beginning of the array definition.  You might fiddle the whole thing by keeping the VB definition as a reference to a variable (rather than the array), then in the VB code that calls the delphi routine pass the FIRST element of the array ( as by ref it will pass the pointer to the array).

Well thats my 2cents worth.
double in VB is double in Delphi... that's been check before...

integer in VB is integer in Delphi.

Also, I pass only the first element of the array, because Delphi actually accepts it as a pointer to the array, and then it works with that...
Can you get a single double ;) value back, rather than an array? I would also be curious if using Dim rather than ReDim makes any difference.

VB should be using a SAFEARRAY internally. By referencing the first element, you should be getting the address of the data portion of the SAFEARRAY, as long as you are in-process. I don't see how your DLL could be out of process.

On the same note, if you want to pass the SAFEARRAY, you could use:
Dim lPtr As Long
lPtr = VarPtr(sindata)

and pass the pointer in as a long. Do you need the C definition of a SAFEARRAY?
The C definition would be good!

tomook --> I think your suggestion will work... It makes alot of sense... I'll try it and see.
typedef struct FARSTRUCT tagSAFEARRAY {
    unsigned short cDims;         // Count of dimensions in this array.
    unsigned short fFeatures;    // Flags used by the SafeArray
                                // routines documented below.
    unsigned long cbElements;    // Size of an element of the array.
                                // Does not include size of
                                // pointed-to data.
    unsigned long cLocks;        // Number of times the array has been
                                // locked without corresponding unlock.
    void HUGEP* pvData;                 // Pointer to the data.
    SAFEARRAYBOUND rgsabound[1];        // One bound for each dimension.
} SAFEARRAY;

Note that the last argument (rgsabound) will have one array element for each dimension of the array, so it will actually be rgsabound[2] in your case. The left-most dimension is rgsabound[0]. SAFEARRAYBOUND is defined as:
typedef struct tagSAFEARRAYBOUND {
    unsigned long cElements;
    long lLbound;
} SAFEARRAYBOUND;

Unless you want to manipulate the SAFEARRAY structure itself, you do not need to worry too much about rgsabound. I just want to reiterate that you may be able to pass it by declaring the argument "As Any", and using "sindata()" in the call. If not, VarPtr is an option, and you pass that as a long. Let me know how it turns out.
ASKER CERTIFIED SOLUTION
Avatar of cd74013
cd74013

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ok... but that does some funky stuff to the the array... I'll try it again though!

Thanks,

Rob.