Solved

Passing an array from Visual Basic to a Delphi DLL

Posted on 1998-07-26
19
516 Views
Last Modified: 2013-11-25
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!
0
Comment
Question by:RWlodarczyk
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 5
  • 3
  • +1
19 Comments
 
LVL 4

Expert Comment

by:tomook
ID: 1466731
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.
0
 

Author Comment

by:RWlodarczyk
ID: 1466732
Edited text of question
0
 

Author Comment

by:RWlodarczyk
ID: 1466733
Adjusted points to 185
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:RWlodarczyk
ID: 1466734
Adjusted points to 195
0
 
LVL 4

Expert Comment

by:tomook
ID: 1466735
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.
0
 
LVL 6

Expert Comment

by:anthonyc
ID: 1466736
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

0
 

Author Comment

by:RWlodarczyk
ID: 1466737
Adjusted points to 200
0
 

Author Comment

by:RWlodarczyk
ID: 1466738
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!
0
 
LVL 6

Expert Comment

by:anthonyc
ID: 1466739
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.
0
 

Author Comment

by:RWlodarczyk
ID: 1466740
I'm not using redim preserve...


0
 
LVL 6

Expert Comment

by:anthonyc
ID: 1466741
but if you are writing a general purpose dll.......nevermind
0
 
LVL 4

Expert Comment

by:tomook
ID: 1466742
Did you try "As Any"?
0
 
LVL 1

Expert Comment

by:cd74013
ID: 1466743
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.
0
 

Author Comment

by:RWlodarczyk
ID: 1466744
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...
0
 
LVL 4

Expert Comment

by:tomook
ID: 1466745
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?
0
 

Author Comment

by:RWlodarczyk
ID: 1466746
The C definition would be good!

tomook --> I think your suggestion will work... It makes alot of sense... I'll try it and see.
0
 
LVL 4

Expert Comment

by:tomook
ID: 1466747
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.
0
 
LVL 1

Accepted Solution

by:
cd74013 earned 200 total points
ID: 1466748
One thing I noticed is that the definition of you call in delphi is a pass by value ( from my limited delphi knowledge ) in order for VB to populate back the values this should surely need the VAR keyword to allow the array to be passed by reference and hence the values placed into the memory by vb being available after return to delphi.

TYPE
   myarray = array of double;


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


0
 

Author Comment

by:RWlodarczyk
ID: 1466749
ok... but that does some funky stuff to the the array... I'll try it again though!

Thanks,

Rob.
0

Featured Post

Online Training Solution

Drastically shorten your training time with WalkMe's advanced online training solution that Guides your trainees to action. Forget about retraining and skyrocket knowledge retention rates.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Most everyone who has done any programming in VB6 knows that you can do something in code like Debug.Print MyVar and that when the program runs from the IDE, the value of MyVar will be displayed in the Immediate Window. Less well known is Debug.Asse…
Background What I'm presenting in this article is the result of 2 conditions in my work area: We have a SQL Server production environment but no development or test environment; andWe have an MS Access front end using tables in SQL Server but we a…
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…

687 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question