Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Calling Delphi DLL functions from VB

Status
Not open for further replies.

sggaunt

Programmer
Jul 4, 2001
8,620
GB
This an update to previous posts, generally things are almost there but there are a couple of issues, that remain, if anyone has any experience of this.

The following function uses a component in the DLL to extract the version information from the DLL.

To explain a little on the development.
I did have a var statement in front of the 'Buffer' parameter as the function uses this to load the getver component with the required filename it will throw an exception if it cannot find the target file.
I added the 'if fileexists' to trap this.

VB does not like the 'var' at all but I have been told that you don't need it in this situation and indeed the function works fine when called from Delphi!!

The problem here is that our customer created a special data type that handles making a VB string out of values returned in this way as this (and other) functions do.

But he does not know how to 'load' the string to be passed in.

Code:
// NOTE VB implementation of passing the filename in to load the getver component has not as yet been found

function EthernetGetDLLVersion(Buffer: PChar): Integer; stdcall;
var MaxLen: integer;
    S: string;
begin
   if Initalised  then
       begin
          if fileexists(Buffer) then
             with DataModule2 do
                 begin
                    getver.fileName := Buffer;
                    s := Getver.fileName;
                    MaxLen := GetDLLVersionLength;
                    if (Buffer <> nil) and (MaxLen > 1) then
                       begin
                          StrLCopy(Buffer, PChar(GetVer.VersionStrings[2]), MaxLen);
                          Result := 0;
                       end
                    else
                       Result := 17;
                 end
         else
             begin
                S := 'Cannot find file '+ S;
                StrLCopy(Buffer, PChar(S), Length(S));
                Result := 18;
             end
       end
   else
      begin
         StrLCopy(Buffer, PChar('DLL is not initalised'), Length('DLL is not initalised'));
         Result := 19;
      end;
end;

Here the parameter V should be a 'single' as it is in the 'Writing' version.
But again he does not know how to access the 'var' parameter unless it is a string!!.

Code:
// IP = string representation of the IP address e.g. '169.20.10.0'
// IEEE conversion functions are in IEE/IEECalc.pas
// NOTE Working implementation (tested with VB)
// Parameter V: Pchar returns the data value as a string No way could be found to access
// this values as var parameter unless it was string !!!!

function  EthernetRead(IP: pchar; Card: integer; Channel: integer; Par: integer; V: Pchar): integer; stdcall;
var Vals: array [1..4] of byte;
    StringVal: string;
    a, E, Counter: integer;
    Value: single;
begin
    // extract the data to the buffer
    E := ReadFrom(IP, 8000, Card);
    if E  > 0 then
       begin
         result := E;
         exit;
       end;
    // wait for response
    DataRxd := False;
    DataUnit3.Timeout := False;
    DataModule2.Response.Enabled := True;

    // Response timer to handle next part
    // wait for it to complete
    Counter := 0;

    // so might this needs to run longer than the timer
    for a := 0 to TimeConstant do
        begin
           if DataRxd then break;
           inc(counter);
        end;

   if DataUnit3.Timeout then
      begin
         Result := 15;    // timed out waiting for data receive
         exit;
      end
    else
      // dont leave the time running
      DataModule2.Response.Enabled := False;

    // now extract the required data from the buffer
    if par <> 100 then
       for a := 1 to 4 do
          vals[a] := ReadData((Channel * 17) + Par + a);
    Value := 0;
    case par of
    0      :  Value := Vals[1]; // Configuration
    1,3    :  Value := Vals[1] or (Vals[2] shl 8);
    5, 9,13: begin
               StringVal := GetIEEEValue(vals[1], vals[2], vals[3], vals[4]);
               Value := IeeeToDecimal(StringVal);
             end;
    100    : Value := ReadData(0); // get the card type
    200..207: Value := ReadData(Par - 199);
    end;

    StringVal := FloatToStrF(Value, FfFixed,3,4);
    StrLCopy(V, PChar(StringVal),  Length(StringVal));

     // if the returned result is none zero then the IEEE value was invalid
    Result := 0;
end;


Steve [The sane]: Delphi a feersum engin indeed.
 
I'm confused, what is the question exactly?

BTW, PChar is a pointer to a character array, which is fine as long as the calling program has the space allocated.

If I recollect (can go look at the book I have if necessary), Visual Basic ought to have a "single" data type of some kind. But is it this issue, or the Visual Basic user not knowing how to handle a "PChar" call? Or am I missing it entirely?
 
This was the original version of the second code section
VB does indeed have a single type, if called with a variable of that type (single) as the 'Value' parameter VB throws an exception.

I suppose the question is Why doesn't this work?
perhaps it is a question for a VB forum?

Our customer seems to want us/me to make our Delphi DLL fit what he knows about VB, but this is resulting in some inelegant constructs, just trying to avoid this.


Code:
function  EthernetRead(IP: pchar; Port: integer;  Card: integer; Channel: integer; Par: integer; Value: Single): integer; stdcall;
var Vals: array [1..4] of byte;
    StringVal: string;
    a, E, Counter: integer;
    Value: single;
begin
    // extract the data to the buffer
    E := ReadFrom(IP, 8000, Card);
    if E  > 0 then
       begin
         result := E;
         exit;
       end;
    // wait for response
    DataRxd := False;
    DataUnit3.Timeout := False;
    DataModule2.Response.Enabled := True;

    // Response timer to handle next part
    // wait for it to complete
    Counter := 0;

    // so might this needs to run longer than the timer
    for a := 0 to TimeConstant do
        begin
           if DataRxd then break;
           inc(counter);
        end;

   if DataUnit3.Timeout then
      begin
         Result := 15;    // timed out waiting for data recieve
         exit;
      end
    else
      // dont leave the time running
      DataModule2.Response.Enabled := False;

    // now extract the required data from the buffer
    if par <> 100 then
       for a := 1 to 4 do
          vals[a] := ReadData((Channel * 17) + Par + a);
    Value := 0;
    case par of
    0      :  Value := Vals[1]; // Configuration
    1,3    :  Value := Vals[1] or (Vals[2] shl 8);
    5, 9,13: begin
               StringVal := GetIEEEValue(vals[1], vals[2], vals[3], vals[4]);
               Value := IeeeToDecimal(StringVal);
             end;
    100    : Value := ReadData(0); // get the card type
    200..207: Value := ReadData(Par - 199);
    end;

  
     // if the returned result is none zero then the IEEE value was invalid
    Result := 0;
end;

Steve [The sane]: Delphi a feersum engin indeed.
 
I went ahead and looked variables up in my Visual Basic book I have here. It seems like they're the same.

Single: IEEE decimal value, 4 bytes


Double: IEEE decimal value, 8 bytes


If I had to guess at the problem, the main VB call definition might be incorrect, or VB might be set to accept some other kind of call than stdcall.

Declare Function GetUserName Lib "advapi32.dll" Alias _
"GetUserNameA" (ByVal lpBuffer As String, ByRef nSize As Integer) As Integer



Hope this helps some?
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top