Solved

I just don't get pChars

Posted on 2001-07-14
12
445 Views
Last Modified: 2010-04-06
Ok, there are a lot of points on this q, but it's because I'm a little dense on pointers, so you may have to explain a little more than you assume.  I know that pChars are pointers, but everytime I use them I spend days trying to get them to work without access violations
or invalid pointer operation errors.

Here's the problem:

I have a record which I create in an EXE.  I then pass a pointer to the record
to a dll (both Delphi).  Since I eventually want the dll to be called by C++,
I am using PChars, instead of Delphi strings.

The dll exports three procedures:

ChangeWithControls
   procedure updates the record, based on what the user entered in the form's controls.

ChangeWithConstants
      procedure changes the record's fields using string constants

ChangeWithStrings
      procedure changes the record's fields using string string variables.

The EXE calls all three procedures and reports the return values of the fields.

Of interest is the fact that the four pChar fields change nicely when modified
by string constants or string variables.  However, when assigned the values entered
in the edit components (pChar(edit1.text), for instance), the values are blotto when
they get back to the EXE.  The fifth pChar, m_file_name, does not get changed by
the dll, so its return value is of no interest, here.

The integers are changed in the dll, only to be sure they can be changed.  They work
just fine.

I have a feeling I am not doing something right when making the assignments for the
four pChar fields, but I don't know what it is.



////////////////////////////////////////////////////////////////////////////////////
Here's the code for the EXE:
unit uExeMain;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

const
   crlf = #10 + #13;

type
   pRhinoParamRec = ^RhinoParamRec;
      RhinoParamRec = packed record
            m_initialized : boolean;

            m_file_name : pChar;

            m_model_linear_units,
            m_output_linear_units,
            m_model_weight_units,
            m_output_weight_units : integer;

            m_draft,
            m_displacement,
            m_trim_degrees,
            m_lcg : pChar;
   end;//RhinoParams

Procedure ChangeWithStrings (params : pRhinoParamRec); external 'pChars';
Procedure ChangeWithConstants (params : pRhinoParamRec); external 'pChars';
Procedure ChangeWithControls (params : pRhinoParamRec); external 'pChars';


implementation

{$R *.DFM}

var
  params : pRhinoParamRec;

procedure TForm1.Button1Click(Sender: TObject);
begin
      new (params);
      with params^ do begin

            m_file_name := 'skiffsolid.gdf';

              m_Draft := '8.0';
      m_Displacement := '';
      m_trim_degrees := '';
      m_lcg := '';

            m_model_linear_units := 0;
            m_output_linear_units := 0;
            m_model_weight_units := 0;
            m_output_weight_units := 0;
      m_initialized := true;
   end; //with params^ do


      with params^ do begin
         ShowMessage
      (  'Before Changing params with string vars.' + crlf +
            'Draft is ' + m_Draft + crlf +
            'Displacement is ' + m_Displacement + crlf +
            'Trim is ' + m_trim_degrees + crlf +
            'LCG is ' + m_lcg + crlf +
         'Filename is ' + m_file_name + crlf +
                  'm_model_linear_units is ' + intToStr(m_model_linear_units) + crlf +
                  'm_output_linear_units is ' + intToStr(m_output_linear_units) + crlf +
                  'm_model_weight_units is ' + intToStr(m_model_weight_units) + crlf +
                  'm_output_weight_units is ' + intToStr(m_output_weight_units)
      );
   end;

      ChangeWithConstants (params);

      with params^ do begin
         ShowMessage
      (  'After ChangeWithConstants.' + crlf +
            'Draft is ' + m_Draft + crlf +
            'Displacement is ' + m_Displacement + crlf +
            'Trim is ' + m_trim_degrees + crlf +
            'LCG is ' + m_lcg + crlf +
         'Filename is ' + m_file_name + crlf +
                  'm_model_linear_units is ' + intToStr(m_model_linear_units) + crlf +
                  'm_output_linear_units is ' + intToStr(m_output_linear_units) + crlf +
                  'm_model_weight_units is ' + intToStr(m_model_weight_units) + crlf +
                  'm_output_weight_units is ' + intToStr(m_output_weight_units)
      );
   end;

      ChangeWithStrings (params);

      with params^ do begin
         ShowMessage
      (  'After ChangeWithStrings.' + crlf +
            'Draft is ' + m_Draft + crlf +
            'Displacement is ' + m_Displacement + crlf +
            'Trim is ' + m_trim_degrees + crlf +
            'LCG is ' + m_lcg + crlf +
         'Filename is ' + m_file_name + crlf +
                  'm_model_linear_units is ' + intToStr(m_model_linear_units) + crlf +
                  'm_output_linear_units is ' + intToStr(m_output_linear_units) + crlf +
                  'm_model_weight_units is ' + intToStr(m_model_weight_units) + crlf +
                  'm_output_weight_units is ' + intToStr(m_output_weight_units)
      );
   end;

      ChangeWithControls (params);

      with params^ do begin
         ShowMessage
      (  'After ChangeWithControls.' + crlf +
            'Draft is ' + m_Draft + crlf +
            'Displacement is ' + m_Displacement + crlf +
            'Trim is ' + m_trim_degrees + crlf +
            'LCG is ' + m_lcg + crlf +
         'Filename is ' + m_file_name + crlf +
                  'm_model_linear_units is ' + intToStr(m_model_linear_units) + crlf +
                  'm_output_linear_units is ' + intToStr(m_output_linear_units) + crlf +
                  'm_model_weight_units is ' + intToStr(m_model_weight_units) + crlf +
                  'm_output_weight_units is ' + intToStr(m_output_weight_units)
      );
   end;
   dispose (params);
end;
////////////////////////////////////////////////////////////////////////////////////





////////////////////////////////////////////////////////////////////////////////////
Here's the code for the DLL:

unit frmpChars;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TForm2 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end; //tForm2


type
   pRhinoParamRec = ^RhinoParamRec;
      RhinoParamRec = packed record
            m_initialized : boolean;

            m_file_name : pChar;

            m_model_linear_units,
            m_output_linear_units,
            m_model_weight_units,
            m_output_weight_units : integer;

            m_draft,
            m_displacement,
            m_trim_degrees,
            m_lcg : pChar;
end;//RhinoParams


Procedure ChangeWithControls (Params : pRhinoParamRec);
Procedure ChangeWithStrings (Params : pRhinoParamRec);
Procedure ChangeWithConstants (params : pRhinoParamRec);

var
  Form2: TForm2;

implementation

{$R *.DFM}


Procedure ChangeWithConstants (params : pRhinoParamRec);
var
      s : string;
begin
      with Params^ do begin
      m_initialized := false;
      m_file_name := strlcat(m_file_name, ' modified.', length(' modified.'));
      m_model_linear_units := 1;
      m_output_linear_units := 2;
      m_model_weight_units := 3;
      m_output_weight_units := 4;
      m_draft := '12';
      m_displacement := '23456';
      m_trim_degrees := '4.33';
      m_lcg := '12.34';

   end;
end;


Procedure ChangeWithStrings (params : pRhinoParamRec);
var
      s : string;
begin
      with Params^ do begin
      m_initialized := false;
      m_file_name := strlcat(m_file_name, ' modified.', length(' modified.'));
      m_model_linear_units := 1;
      m_output_linear_units := 2;
      m_model_weight_units := 3;
      m_output_weight_units := 4;
      s := '12';       m_draft := pChar(s);
      s := '23456';    m_displacement := pChar(s);
      s := '4.33';     m_trim_degrees := pChar(s);
      s := '12.34';    m_lcg := pChar(s);
   end;
end;


Procedure ChangeWithControls (Params : pRhinoParamRec);
begin
      form2 := tForm2.create (application);
      with Params^ do begin
   with form2 do begin
      showModal;
      m_initialized := false;
      m_file_name := strlcat(m_file_name, ' modified.', length(' modified.'));
      m_model_linear_units := 1;
      m_output_linear_units := 2;
      m_model_weight_units := 3;
      m_output_weight_units := 4;
      m_draft := pChar(edit1.text);
      m_displacement := pChar(edit2.text);
      m_trim_degrees := pChar(edit3.text);
      m_lcg := pChar(edit4.text);
   end;
   end;
end;


procedure TForm2.Button1Click(Sender: TObject);
begin
      close;
end;

end.

////////////////////////////////////////////////////////////////////////////////////


0
Comment
Question by:cestes001
12 Comments
 
LVL 13

Accepted Solution

by:
Epsylon earned 300 total points
ID: 6282185
Why don't you use String in the record instead of PChar? Because you are passing a pointer to the record to the dll-functions it doesn't matter what you put there.

However, String and PChar are only pointers when used in a record. Use String[255] or array[0..1023] of Char to make it a physical part of the record.

And do not use strlcat on contants.
0
 

Author Comment

by:cestes001
ID: 6282260
Epsylon:

Thx for quick response.  

Here comes the dense part:  If I declare the pChar vars as string[255] the program works.  However, is this going to work with a C++ caller?  Are the string[255] vars null terminated?

Inspecting the value of Params^.m_draft[255] yields #0.
Inspecting the value of Params^.draft[4] (after entering '123' in the appropriate edit control) yields 'e'.  
Inspecting the value of Params^.draft yields '123', which leads me to suspect that the string temination is being detected by the length byte, not the null-terminator.  If this is the case, how will the C++ caller know to stop at the '3'.  Do I have to manually add the null terminator?

I told you that more explanation would be necessary.  :-)

Thx, again.

0
 
LVL 20

Expert Comment

by:Madshi
ID: 6282295
You could declare the strings as "array [0..XXX] of char" (zero terminated string buffer), that is nice for all languages.

About the reason why ChangeWithControls fails? Hmmm... I could explain that in case you would have a TForm2.Free or .Release in the end of your ChangeWithControls function. Do you have it there? Then why did you not post it here? If you don't have it there, you have a memory leak, because you're creating TForm2 without freeing it anywhere.

Regards, Madshi.
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6282401
Then 'array [0..XXX] of char' as Madshi and I already suggested, or dynamic memory allocation (AllocMem, ReallocMem and FreeMem) are the only options, I think.
0
 

Author Comment

by:cestes001
ID: 6282440
Ok, here comes the dense part in overtime.

If I delcare the 'strings' as
array[0..255' of char
0
 

Author Comment

by:cestes001
ID: 6282445
oops, hit the wrong button.

Again:

If I declare the strings as
array[0..255] of char

How do I then make the assignment
params^.m_draft := edit.text;

0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 13

Expert Comment

by:Epsylon
ID: 6282460
With something like:

strpcopy(m_file_name, Form2.Edit1.Text);


Note that the 2 dll function are not members of the TForm2 object, so you need to use Form2.Edit1.Text.
0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6282504
I think C++ has an equivalent for OleVariant, so you can use that too........
0
 

Expert Comment

by:Antero
ID: 6282597
My Comments:

1. ChangeWithConstants works only, because you are doing statical linking to your DLL.

If you change to do dynamical linking
(not use the import unit, eg:
procedure ChangeWithConstants; external 'MYDLL.DLL';

but instead do it with:
LoadLibrary
GetProcAddress
and do
FreeLibrary
before showing the results, you get at least garbage, and it is likely to get an Access Violation.
Why?
Because doing m_draft := '12';
actually sets the PChar in your record to point to the string '12' in the code segment of your DLL.
If you then free your DLL by using FreeLibrary
(can only be done if you used LoadLibrary to load it)
that makes your PChar invalid !

The general problem is that your DLL , as it is, just sets the PChar's to point to somewhe which will not stay!
Remember, whereas String's are managed by Delphi, PChar is just a pointer, and YOU need to take care where actually you set them point to.
for example:

     s := '12';       m_draft := pChar(s);
     s := '23456';    m_displacement := pChar(s);

first sets m_draft to point to string s's contents location.
But, when you execute that 2nd line, *either*:
the previous location of '12' is now free memory, leaving
m_draft to point to somewhere that may change whenever
because it is not allocated anymore, *or*:
will immediately overwrite that '12' with '23456'.
This is even more likely if the 2nd string is shorter than the 1st.

As someone suggested, declaring them all
like
m_draft : Array[0..127] of char
is a good solution, and, then you can use m_draft
as if it were PChar, or if you need to you may even do
typecasting like this
var
  P:PChar
begin
  P := PChar(m_draft);
end;

As long as you are careful to:
1. NOT to set that P to point to everywhere else than draft
and
2. NOT to put anything over 127 characters to your m_draft
(unless you change the declaration) (or 128, if you include the terminating null in the count)

you should be fine.

If you need really long strings and you don't know in advance (at design time), how long maximum, then you have two options:
1. Look the help using keyword VirtualAlloc
or
2. in COM (Component Object Model) the is a type called COM string. I would guess that it *should* take care of proper memory allocation, but if you choose to do that, be sure to read a good book about COM...



0
 

Author Comment

by:cestes001
ID: 6284144
All comments appreciated.  Epsylon, you win the Cupie (?) doll.  I don't know how to give you the 300 pts, but I suspect you need to post your large comment as an answer.  Perhaps you could also include the follow-ons to aid in future reference to the question.

I declared the vars as array[] of char and used strPCopy for assignment of values.  No A/Vs and no lost data.  However, this is only when a Delphi Exe calls my Delphi Dll. It remains to be seen whether the C++ guy will be happy with what I've given him, but, hopefully, he will be in on Monday and I can put the whole thing to the test.

BTW: does anyone know why I am not getting email notifications?

Thanx all for the help.  

0
 
LVL 13

Expert Comment

by:Epsylon
ID: 6286152
Hi, let's wait until the C++ guy has a smile on his face   :o)
If you want, you can accept any comment as answer. See the buttons on the right side of each comment.


> does anyone know why I am not getting email notifications?

There are some mayor problems with the notifs:

http://www.experts-exchange.com/jsp/qShow.jsp?ta=commspt&qid=20147325

I appears to get worse instead of getting better  :o(
0
 

Author Comment

by:cestes001
ID: 6407974
Ok, the C++ guy pushed me into using COM, instead.  Whole different set of rules.

Thanx for the efforts, guys.

0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

762 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

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now