Thread: pChar in dll

    #1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2014
    Posts
    4
    Rep Power
    0

    Question pChar in dll


    Hello,

    I have a strange problem with passing a pchar parameter.

    A software that we have allows to use a dll to manipulate a value that it passes as pChar to the said dll.

    So I am writing a very simple dll that should take the pchar value, search in a list of values (saved on disk as text file, a.txt) and return the corresponding value (same line number) from another list (saved on disk as text file, a.txt).

    The code is

    Code:
    library replace;
    
    
    uses
      ShareMem,
      SysUtils,
      dialogs,
      Classes;
    
    {$R *.RES}
    procedure repl1(  var pchText : pchar);
    var
    
    slr: TStringList;
    sls :Tstringlist;
    stringA : string;
    stringB : string;
    stringC : string;
    linea : integer;
    
    begin
    slr := nil ;
    sls := nil ;
    slr := TStringList.Create;
    sls := TStringList.Create;
    stringA:='';
    stringB:='';
    stringC:='';
    linea := 0;
    
    slr.LoadFromfile('a.txt');
    sls.LoadFromfile('b.txt');
    stringA :=  pchtext;
    linea := slr.indexof(stringA);
    if linea > -1
    then
    begin
    stringB := sls[linea];
    stringC := stringB;
    pchText := pchar (stringC) ;
    end
    
    else
    begin
    pchtext := '';
    end;
    slr.LoadFromfile('a.txt');
    sls.LoadFromfile('b.txt');
    linea := 0;
    stringA:='';
    stringB:='';
    stringC:='';
    slr.free ;
    sls.free ;
    
    end ;
    
    
    
    procedure repl( var pchText1 : pchar);   stdcall;
    
    begin
    repl1 (pchText1);
    end ;
    
    exports
    repl;
    
    begin
    end.
    now, I think there may be a more effective way to do it, but I can't find anything else that works... the code that I wrote works only when the text file a.txt and b.txt contains up to 10 lines. If I try to create bigger lists , the pchar value is not manipulated (so the main program that calls the dll , at the end displays the initial value that it passes).

    I can't see nor change the source code of the main program... you have any hint?

    Thanks a lot
  2. #2
  3. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2014
    Posts
    4
    Rep Power
    0
    ... additionally: if I remove the two lines

    slr.free ;
    sls.free ;

    at the first call of the DLL, the value B is correctly passed to the main software. But after the second call, the program crashes...

    Thanks,
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    397
    Rep Power
    0
    You can avoid the whole problem of passing PChars to your DLL by simply using WideString instead of PChars. If I read your requirements correctly, pass a string to the dll, the dll is to search for that value in a.txt, if it finds it, return the string at the same line position from b.txt, then something like this should work.


    Code:
    library replace;
    
    uses
      //ShareMem, //Only needed if you pass Strings
      SysUtils,
      dialogs,
      Classes;
    
    {$R *.RES}
    
    procedure repl(var Texta: WideString); stdcall;
    var
      SearchFor: String;
      Filea: TStringList;
      Fileb: TStringList;
      I:  Integer;
    begin
      SearchFor := String(Texta);//not needed, but makes the conversion explicit
      TextA := '';//Assume we don't find anything
      Filea := TStringList.Create;
      try
        Filea.LoadFromFile('a.txt');
        I := Filea.IndexOf(SearchFor);
        if I >= 0 then
        begin
          Fileb := TStringList.Create;//Only create 2nd List if item is found
          try
            Fileb.LoadFromFile('b.txt');
            if Fileb.Count >= I then //make sure there actually is an Ith Line in the file
               TextA := WideString(Fileb[I]);
          finally
            Fileb.Free;
          end;
        end;
      finally
        Filea.Free;
      end;
    end;
    
    exports
    repl;
    
    begin
    end.
    how to use
    Code:
    var
      MyString: String;
      MyReturnString: String;
      DLLString: WideString;
    begin
      MyString := 'Hello World';
      DLLString := WideString(MyString);
      MyReturnString := String(repl(DLLString));
      if MyReturnString <> '' then
      begin
        //you got something back
      end;
    end;
    Last edited by majlumbo; November 23rd, 2015 at 09:43 AM.
  6. #4
  7. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Sep 2014
    Posts
    4
    Rep Power
    0
    hi majlumbo, thanks for the reply.
    the problem is that I can't change the code of the calling program , because it's a commercial software and in its documentation it only says that it can interface a dll passing a pchar parameter and getting it back at the end of the procedure
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    397
    Rep Power
    0
    Sorry about that, figured you had control over both the dll and the calling application.

    I think the problem you are having is based on the fact that you use a PChar much like you would use a string. A PChar is basically a pointer to dynamic array of characters, which means you have to allocate the size of the array before assigning to it. For you, I assume that means the calling procedure will do the allocation, and subsequent de-allocation. If that turns out to be incorrect, then the DLL will have to allocate, but then the DLL will also have to be used to de-allocate, which is not so simple, since you obviously would need to return the value before you de-allocate. That would mean a second call to the DLL with a pointer to the same PChar to de-allocate it. (You cannot have one code section allocate the memory and another de-allocate it - DLL vs. calling executable).

    Specifially, when you assign a value to your pchar is probably the cause your issues.

    Code:
    linea := slr.indexof(stringA);
    if linea > -1 then
    begin
      stringB := sls[linea];
      stringC := stringB;
      pchText := pchar (stringC) ;<-- here
    end
    hopefully, this works for you... I don't have Delphi with me to test, plus I obviously don't have your commercial calling application

    Code:
    library replace;
    
    uses
      //ShareMem, //Only needed if you pass Strings
      SysUtils,
      dialogs,
      Classes;
    
    {$R *.RES}
    
    procedure repl(Texta: PChar); stdcall;//I think you don't need (var Texta: PChar) because a PChar is already a Pointer.
    var
      SearchFor: String;
      Filea: TStringList;
      Fileb: TStringList;
      I:  Integer;
    begin
      SearchFor := String(Texta);
      Filea := TStringList.Create;
      try
        Filea.LoadFromFile('a.txt');
        I := Filea.IndexOf(SearchFor);
        if I >= 0 then
        begin
          Fileb := TStringList.Create;//Only create 2nd List if item is found
          try
            Fileb.LoadFromFile('b.txt');
            if Fileb.Count >= I then //make sure there actually is an Ith Line in the file
               StrPCopy(Texta, Fileb[I])
               //assumes that Texta is big enough to hold what ever is in Fileb[I]
               //Can cause problems if it isn't, so I have to assume that
               //your calling procedure is already handling making TextA variable
               //large enough for you...
            else
              StrPCopy(Texta, '');
          finally
            Fileb.Free;
          end;
        end
        else
          StrPCopy(Texta, '');
      finally
        Filea.Free;
      end;
    end;
    
    exports
    repl;
    
    begin
    end.
    Also, depending on the definition of a PChar for the calling procedure, you may in fact need to use PANSIChar instead of PChar (PANSIChar points to characters that are 1 byte in length while a PChar in modern Delphi's point to characters that are 2 bytes). If that is the case, then you'll also need to work with ANSIString instead of String types. TStringList does NOT have an equivalent TANSIStringList, so you'll need to possibly convert from String to ANSIString when you find your values in it.

IMN logo majestic logo threadwatch logo seochat tools logo