VB Calling Delphi

I have a VB DLL that is calling a Delphi DLL to retrieve a string. It seems to work fine with short strings but every once and a while with a longer string (> 255) I get an exception and the VB app crashes.

The Delphi function is defined as follows:
function WebSessionStatus(pSession, D: string): string; stdcall;

It returns the string as follows:
S := S + '~#@#@'
p := StrAlloc(length(S) + 1);
Result := StrPCopy(P,S);

Is it the definition of string that is causing my problem?
How can this be done so that the function works correctly every time?
trgriebAsked:
Who is Participating?
 
MadshiCommented:
>> The Delphi code looks like this:
>> function CallDelphi(parm: ansistring; resultbuffer pchar): integer
>> Note that if I defined parm as string, it blew up. Ansistring solved that.

That is quite suspiscous. Why are you using AnsiString there at all? As I said, even if you get no exceptions, the reference counting will get confused. Please use pchar instead.

Delphi's help says, that string and AnsiString are identical, if the compiler check {$H+} is set (this is the default). Only if you set {$H-}, string and AnsiString are different. But that is not the default.

Regards, Madshi.
0
 
scrapdogCommented:
Did you write the Delphi DLL?  If you can change the Delphi DLL, you will to do so.

You have two choices:

1.

DLLs in Delphi are not very friendly with strings, unless you include the ShareMem unit in your uses clause (as the first unit listed).  If you do this, you will also have to distribute BORLANDMM.DLL with your application.  Somehow VB will have to use this DLL as well (I am not sure how you do this).

If you can be certain that the strings are going to be short strings, you do not need to do this.


2.

An alternative is to redeclare your function to accept and return "PChar" rather than string.  If you do this, you won't be directly working with strings and do not need to include ShareMem and BORLANDMM.DLL.

However, you do have to go through the tedium of converting PChars to strings and vice versa.
0
 
scrapdogCommented:
>you will to do so

I mean, you will have to do so
0
Cloud Class® Course: Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

 
MadshiCommented:
I don't think you can make VB use borlndmm.dll correctly. The usual way to make your Delphi dll work with other languages is to use pchar, like scrapdog already pointer out. But please note, that you have to care about who allocated and who frees the pchar strings. Look how Windows APIs solve this problem. E.g. when calling GetWindowText or GetComputerName or GetUserName or most other string returning functions, you must give a buffer, in that the API then copies the string characters. Most of the time the maximal buffer size is either predefined or you have a possibility to ask which size is needed. E.g. when calling RegQueryValueEx you can ask how big the buffer has to be.
Then there are some APIs (only a few) which allocate a buffer for you. E.g. some of the NT network related APIs. In that case you need to free the buffer later by calling NetApiBufferFree.
A third approach would be that you allocate the buffer with LocalAlloc and that the caller needs to free it with LocalFree again.

You can use either approach, but you have to make it clear, so the VB programmer knows what to do.

Regards, Madshi.
0
 
ziolkoCommented:
In "Delphi DLL" use WideString not string, then You will have no problems with length of string and no problem with allocateing/deallocateing buffers.
ziolko.
0
 
MadshiCommented:
Hi ziolko,

I don't think so. Why should a wideString work better than a normal string? I see no reason for that.

Regards, Madshi.
0
 
ZereterCommented:
The WideString is defined as "WideStrings are dynamically allocated with a maximum length limited only by available memory" (quoted from Delphi 5 help) Wouldn't that take care of the string limitation. Also Delphi has a AnsiString or LongString which is basicly a pointer.
0
 
MadshiCommented:
The type "string" that is used in the original question text is the same as an AnsiString or LongString, these types are all identical (except if you have set very strange Delphi options). A wideString is basically the same as a "string/AnsiString/LongString", except that it uses 2 bytes per character, not one. So neither wideString, nor AnsiString nor LongString really makes a difference to what was used in the original question text. All those types are not compatible with any other language. They're purely Delphi contructs, which furthermore need that ugly "borlndmm.dll" if you use a Delphi application/dll combo, which wants to use strings/wideStrings. So all this stuff is really not the correct solution here, at least IMHO...   :-/

Regards, Madshi.
0
 
ziolkoCommented:
Useing string causes access violation errors, WideString not. Also COM/DCOM DLLs use WideString, I supose WideString is more "friendly" for Windows (don't ask me why)
ziolko.
0
 
MadshiCommented:
>> Useing string causes access violation errors, WideString not.

I don't believe that. Both cause access violation when used improperly.

>> Also COM/DCOM DLLs use WideString

Heh? COM DLLs use PWideChar, not WideString!!!

>> I supose WideString is more "friendly" for Windows (don't ask me why)

Windows doesn't know Delphi's WideString format, Windows just knows a zero-terminated wideChar array -> PWideChar.

Regards, Madshi.
0
 
ziolkoCommented:
Madshi maybe You are right I don't know, all I know is that when I use WideString I don't have problems like when useing string.(of course the best way is to use PChar but WideString is easier, You don't have to worry about allocateing/deallocateing memory)
ziolko.  
0
 
MadshiCommented:
Maybe, but the problem is that if you do this in Delphi:

function GiveMeAString : WideString;
begin
  result := ...;
end;

exports GiveMeAString;

And then declare this function differently in C++ or VB, then Delphi's reference counting gets confused. The function "GiveMeAString" increments the reference counter, and the caller of this function normally decrements it again, if it is not needed anymore. But since C++ and VB don't know Delphi's string logic, they never decrement the reference counter. As a result the string is never freed anymore. So you not only have to worry about allocating/freeing, you can't even prevent memory leaks when using WideString/String like this.
Maybe you won't even get crashes, maybe it *seems* to run fine. But believe me: at least you have a built in memory leak, everytime C++ or VB calls your function...   :-(

Regards, Madshi.
0
 
MadshiCommented:
Hmmm... If I tried calling the GiveMeAString function as if it would be "PWideChar", I get an exception. ziolko, how are you declaring the function in C++ or VB? Which string type do you use? I don't understand why you don't get an exception... Hmmm... Strange...
0
 
ziolkoCommented:
Madshi >> Which string type do you use?
Please give me couple of days to check it out, all my VB projects are back in office (I don't write in VB very often so I need look into source code) and right now I have my vacation.
ziolko.
0
 
trgriebAuthor Commented:
I have gotten this to work with the following code.

Similar to the techniques used in calling the Windows API, the VB calling function needs to set up a buffer for the string and then pass a pointer to that buffer. You can do this in VB by using ByVal in the call. The Delphi module then fills that buffer rather than returning a result.

The VB code looks like this:
Declare Function CallDelphi Lib "thedll.dll" (Byval parm as string, byval returnbuffer as string) as long

Dim resultbuffer as string
Dim returncode as long

resultbuffer = string$(2000, " ")
returncode = CallDelphi("parm string", resultbuffer)

The Delphi code looks like this:
function CallDelphi(parm: ansistring; resultbuffer pchar): integer
s: string;

Note that if I defined parm as string, it blew up. Ansistring solved that.

Then you just fill the buffer

s := "Some string to return. Limited to the 2000 character buffer size";
StrPCopy(resultbuffer, s);
Result := 0;

And everything works fine.

Thanks for all the input.
0
 
ziolkoCommented:
Damn!! Madshi You are right!!! even if I get no exceptions, there are memory leaks!!!.
ziolko.
0
 
MadshiCommented:
:-)  How did you test that? Were you able to reproduce the leaks? I'm just asking for my personal interest...
0
 
trgriebAuthor Commented:
Well I certainly don't want any memory leaks.

I don't understand why it began working when the only change I made was from string to ansistring. I don't even know how to change the default setting.
0
 
MadshiCommented:
Don't understand the ansistring<->string, either. Please just try it with pchar. It should work, too. And you won't have any memory leaks this way...
0
 
ziolkoCommented:
Madshi > WinNT's TaskManager shows it clearly
ziolko.
0
 
ziolkoCommented:
Madshi > WinNT's TaskManager shows it clearly
ziolko.
0
 
ziolkoCommented:
Is this Q still open?
ziolko.
0
 
Russell LibbySoftware Engineer, Advisory Commented:
No comment has been added lately, so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area that this question is:

Accept madshi's comment as answer

Please leave any comments here within the next seven days.
 
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!
 
Thank you,
Russell

EE Cleanup Volunteer
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.