The DataCAD Developer Network (DDN) is an online resource for information and support for DCAL® (DataCAD Applications Language) developers as well as anyone interested in creating fonts, toolbars, hatch patterns, or linetypes for use in DataCAD.

Moderator: pjdixit

#72127 by dhs
Sat Aug 19, 2017 12:41 am
Hi,
I'm having a problem using mode_group. It seems to always crash with the following exception message:
Project DCADWin.exe raised exception class $C0000005 with message 'access violation at 0x00000000: read of address 0x00000000'

A snippet of the original code that was causing this problem is shown below:
Code: Select all      mode_init1 (l.mode);
      if ent_near (ent, l.getp.curs.x, l.getp.curs.y, l.mode, true) then begin
         mode_init1 (l.mode);
         mode_group (l.mode, ent);
         ProcessSelection;
         l.state := 101;
      end;

When I stepped through this with the debugger ent_near appeared to be returning the expected entity in ent (I checked enttype & entcolor and they were the expected values) but the code crashed on the mode_group call.

In an effort to find what the problem was I tried using a different mode variable for the second mode logic (couldn't see any problem with re-using the same one, but was willing to clutch at any straw), and I also added an ent_move and an ent_update just to make sure that the ent being returned by ent_near was a valid entity. The result of this was the following code snippet:
Code: Select all      mode_init1 (tempmode);
      if ent_near (ent, l.getp.curs.x, l.getp.curs.y, tempmode, true) then begin
         ent_move (ent, 32, 32, 32);
         ent_update (ent);
         mode_init1 (l.mode);
         mode_group (l.mode, ent);
         ProcessSelection;
         l.state := 101;
      end;

Once again I stepped through the above with the debugger. The ent_move and ent_update executed without problem and I was able to check that the x,y,&z values of ent.linpt1 had all increased by 32 as expected. But the mode_group call is still crashing.

I'm hoping that Dave can throw some light on this... hopefully there is something wrong with my code snippets above. (Dave: If you're willing to look into this I have already sent you a dmx file that will reproduce this problem - it is in the same file that I sent you for the getpoly problem [just choose F2 in the menu of the dmx I sent you and then select an entity]).
#72133 by Jsosnowski
Sun Aug 20, 2017 8:58 am
I noticed that you used mode_group in your shadow macro. Is that where the bug occurs? It appears to work in my DCAD12 . Is it possible that difference is in the number of entities in the group?
#72134 by dhs
Sun Aug 20, 2017 4:22 pm
mode_group works just fine in Classic DCAL (that I used for the Shadow macro).
It is in DCAL for Delphi that the bug exists. This makes it impossible to write any D4D macro that wants to implement the normal selection menu.
#73308 by Mark F. Madura
Thu Feb 22, 2018 4:50 pm
I haven't had a change to consult with Dave on this, but it might be a syntax issue. Here's an example taken from PSymColors.dpr which is included in the DCAL for Delphi samples.

Code: Select allFunction DCAL_Main(act: action; pl, pargs: Pointer): wantType;
var
   retval: asint;
   l: PDCMainL;
   mode : mode_type;
BEGIN
   l := PDCMainL(pl);
   if act = aagain then
   begin
      case l.state of
         1:
            begin
               if l.getp.Result = res_escape then
               begin
                  case l.getp.key of
                     f5: PGSavevar.srch := not PGSavevar.srch;
                     s0: l.state := 0;
                  end;
               end
               else if l.getp.Result = res_normal then
               begin
                  mode_init1(mode);
                  mode_lyrlocked(mode, false);
                  mode_enttype(mode, entsym);
                  if ent_near(l.ent,l.getp.curs.x,l.getp.curs.y,mode,true) then
                  begin
                     l.state := 2;
                  end;
               end;
            end;
         2: begin
               if not frm.DoSymbolEntity(l.ent) then
                  l.state := 1;
            end;
         else l.state := 0;
      end;
   end;
   if act = afirst then
   begin
      l.state := 1;
      wrterr('PSymColors Macro BETA - Version '+ MacroVersion, True);
      frm := TForm1.Create(Application);
   end
   else if act = alsize then
   begin
      SetLocalSize(sizeof(l^));
   end;
   if act <> alsize then
   begin
      wrterr('Select symbol');
      case l.state of
         1:
            begin
               wrtlvl('CHNG CLR');
               wrtmsg('Select symbol from drawing');
               lblsinit;
               lblsett(5,'Lyr Srch',PGSavevar.srch);
               lblson1;
               getpoint(l.getp, retval);
            end;
         2 : begin
               callnone(retval);
            end;
         else begin
            retval := XDone;
            frm.Free;
         end;
      end;
   end;
   Result := retval;
end;
#73309 by dhs
Fri Feb 23, 2018 3:01 pm
Hi Mark,

I don't think it is a syntax issue, but I'm a bit confused by the example you included from PSymColors as it does not call mode_group at all.

I have only been able to find one example of the use of mode_group call in the sample source code, and that is in the UAUTOVOID unit in the AEC_Model sample.
Unfortunately I don't seem to be able to get the AEC_Model macro to call this line of code ... it is in the remove functionality, so I tried creating some windows and then removing them, but the remove functionality didn't seem to work: it kept telling me it could not find a Window/Door/Cutout to remove (I haven't done a thorough analysis, but it seems like the logic that is asking you to select the window/door to remove is ignoring entities with the '*AEWINDDOOR' attribute - and as that attribute is added to windows/doors created by the macro it is not able to select them for removal ???).

I am not the first person to have problems with the mode_group call: There were problems with in when Pietro was writing his book as he also noted that the only result he could obtain with mode_group was a crash.

When I was debugging it the code right up to the mode_group call exectuted correctly but it crashed on the mode_group call itself.

Regards,
David H
#73310 by Mark F. Madura
Fri Feb 23, 2018 4:11 pm
Sorry David,

I got sidetracked by the mode_type record definition.

mode_group is an export of DataCAD's internal procedure mode_shape.

Code: Select allprocedure mode_shape(var mode: mode_type; ent: entity);
var
   ent1: entity;              { don't change parameter }
begin
   mode.shape := True;
   mode.lyr_mode := 3;           { don't assume layers }
   ent1 := ent;
   ent_shape(ent1);
   mode.entaddr := ent1.ptr;     { start with this entity }
end;


There's only a few places in DataCAD where this procedure is used. One of them is in a procedure we call KnockOut_Move_to_Front.

Code: Select allprocedure KnockOut_Move_to_Front(var ent2move : entity);

var
   ent   : entity;
   mode  : mode_type;
   addr  : lgl_addr;
   numselect : asint;

begin
   ent := ent2move;
   ent_shape(ent);
   mode_init (mode);
   mode_shape (mode, ent);  // Exported as mode_group
   addr := ent_first (mode);
   WHILE ent_get (ent, addr) DO BEGIN
      addr := ent_next (ent, mode);
      ssadd(14, ent);
   end;
   addfrom (ent2move.layer, numselect, false, 14);
   ssClear(14);
end;


Maybe this example will help.

MFM
#73313 by dhs
Fri Feb 23, 2018 6:22 pm
Hi Mark,
I have emailed you the source of the project where I was experiencing this problem. Had some problems emailing it so please let me know if you don't get it (and apologies if you received it multiple times!)
David
#73318 by Mark F. Madura
Mon Feb 26, 2018 7:58 am
Now that I'm enlightened on the subject, I can share the following source to illustrate the use of mode_group. In this example, the user is prompted to select an entity. The macro then collects all of the entities that belong to the same group as the selected entity and changes their color to red. I've also added a Layer Search toggle to demonstrate the use of mode_init1 to initialize mode to DataCAD's current layer search state. Here's the code:

Code: Select alllibrary Edit_Group;

uses
  // Windows
  {Windows,} {Messages,} {SysUtils,} {Variants,} {Classes,} {Graphics,} {Controls,} {Forms,} Dialogs,

  // DCAL for Delphi
  UConstants in '..\..\Header Files\UConstants.pas',
  // UDCLibrary in '..\..\Header Files\UDCLibrary.pas',
  UInterfaces in '..\..\Header Files\UInterfaces.pas',
  UInterfacesRecords in '..\..\Header Files\UInterfacesRecords.pas',
  URecords in '..\..\Header Files\URecords.pas',
  UVariables in '..\..\Header Files\UVariables.pas';

{$E dmx}  // dmx is the extension for DCAL Dll's
{$R *.res}

const
  MacroVersion = '1.0.0.1';

type
  EditGrpL = record
    state: asint;
    ent: entity;
    mode: mode_type;
    addr: entaddr;
    case byte of
      0: (getp: getpointArg);
  end;

  PEditGrpL = ^EditGrpL;

function EditGrp_main(act: action; pl, pargs: Pointer): wantType;

var
  retval: asint;
  l: PEditGrpL;
  // ent : entity; // Defined in EditGrpL
  // mode: mode_type; // Defined in EditGrpL
  // addr: entaddr; // Defined in EditGrpL
  // pt : point;   // Defined in EditGrpL (getp.curs)

begin
  l := PEditGrpL(pl);

  if act = aagain then begin
    case l.state of
      1: begin
          if l.getp.Result = res_escape then begin
            case l.getp.key of
              f5: PGSavevar.srch := not PGSavevar.srch;
              s0: l.state := 0;
            end;
          end
          else if l.getp.Result = res_normal then begin
            mode_init1(l.mode); // Use mode_init1 to initialize mode to DataCAD's current layer search state
            if ent_near(l.ent, l.getp.curs.x, l.getp.curs.y, l.mode, true) then begin
              showmessage('Entity Found');  // Winddows message dialog
              // ent_group(ent);  // Makes the entity you select, the first entity in the group.
              mode_init(l.mode);
              mode_group(l.mode, l.ent);
              l.addr := ent_first(l.mode);  // Get the first entity (in the group).
              while ent_get(l.ent, l.addr) do begin
                l.addr := ent_next(l.ent, l.mode);
                ent_draw(l.ent, drmode_black);
                l.ent.color := clrred;
                ent_update(l.ent);
                ent_draw(l.ent, drmode_white);
              end;
            end
            else begin
              showmessage('No Entity Found');
            end;
          end;
        end;
    else l.state := 0;
    end;
  end;

  if act = Afirst then begin
    wrterr('Edit Group Macro (DCAL for Delphi) - Version' + MacroVersion, true);
    l.state := 1;
  end

  else if act = Alast then begin
    // If you're going to get blown off the stack...
    // (i.e.) the user pressed a hotkey,
    // then this is your last chance to clean up temporary data.
  end

  else if act = AlSize then begin
    SetLocalSize(sizeof(l^));
  end;

  if act <> AlSize then begin
    wrterr('Select Entity');
    case l.state of
      1: begin { 1: Main Menu }
          wrtlvl('Edit Group');
          wrtmsg('Select entity within group to change.');
          lblsinit;
          lblsett(5, 'Layer Search', PGSavevar.srch);
          lblson1;
          getpoint(l.getp, retval);
        end;
    else retval := XDone;
    end;
  end;
  Result := retval;
end;

function Main(dcalstate: asint; act: action; pl, pargs: Pointer): wantType; stdcall;
begin
  case dcalstate of
    XDcalStateBegin: Result := EditGrp_main(act, pl, pargs);
  else Result := XDone; { Necessary }
  end;
end;

exports
  Main;
{ This is the entry function that will be called by DataCAD.
  All DCAL Dlls will have this function. }

begin

end.
#73319 by Mark F. Madura
Mon Feb 26, 2018 8:09 am
From your original example:

Code: Select all      mode_init1 (l.mode);
      if ent_near (ent, l.getp.curs.x, l.getp.curs.y, l.mode, true) then begin
         mode_init1 (l.mode);
         mode_group (l.mode, ent);
         ProcessSelection;
         l.state := 101;
      end;


The corrected method:

Code: Select all      mode_init1 (l.mode);
      if ent_near (ent, l.getp.curs.x, l.getp.curs.y, l.mode, true) then begin
         mode_init (l.mode);
         mode_group (l.mode, ent);
         addr := ent_first(l.mode);
         while ent_get(ent, addr) do begin
            addr := ent_next(ent, l.mode);
            ProcessSelection; // Process current entity or add to selection set.
         end;
         l.state := 101;
      end;
#73325 by Jsosnowski
Mon Feb 26, 2018 7:24 pm
Mark found the solution.

The problem is with the declaration of the mode_group declaration in uinterfaces.pas.

The errant line reads :
Code: Select all  TPFmode_group = procedure(var mode: mode_type; var ent: entity); stdcall;

while the corrected version reads:
Code: Select all  TPFmode_group = procedure(var mode: mode_type; ent: entity); stdcall;
.
To quote Mark:
In this case, the ent variable should never be passed as a 'var' and bad things happen if you do.


To fix this delete the 'var' declaration in the argument (approx. line 155) in the header file uinterfaces.pas.
#73329 by dhs
Tue Feb 27, 2018 5:23 am
Note: There was never an error in the mode_group procedure.


Sorry to get caught up on semantics Mark. The error may not have been in the mode_group procedure itself, but it was in the supplied interface file (uinterfaces.pas).

My code works when I compile it using the new header files you supplied (the code to get the address of the first entity and iterate through the loop was all in the ProcessSelection procedure since that procedure was designed to iterate through the entities for a mode that could have been set up by other selection methods as well.

Regards,
David H
#73331 by Mark F. Madura
Tue Feb 27, 2018 8:34 am
No problem David, you're right. I went back and compared the histories of the DataCAD unit that exports mode_shape as mode_group, and the D4D header file that declares it.

mode_group was first declared in uInterfaces for DataCAD 11:
Code: Select all  procedure mode_group(var mode: mode_type; var ent: entity); stdcall; external appname;

The declaration of mode_group was changed for DataCAD 12:
Code: Select all  TPFmode_group = procedure(var mode: mode_type; var ent: entity); stdcall;

This has been the declaration in uInterfaces ever since.

In DataCAD 8 through 10, mode_shape was declared as follows:
Code: Select all  procedure mode_shape(var mode: mode_type; var ent: entity);

In DataCAD 11: stdcall; was added
Code: Select all  procedure mode_shape(var mode: mode_type; var ent: entity); stdcall;

In DataCAD 12: var was removed from ent
Code: Select all  procedure mode_shape(var mode: mode_type; ent: entity); stdcall;

DataCAD's internal mode_shape was first exported as mode_group in DataCAD 19.

Ironically, there has been a comment next to the entity parameter in mode_shape since DataCAD 8.
Code: Select all  { don't change parameter }

It should probably be changed to:
Code: Select all  { don't change parameter - OR BAD THINGS WILL HAPPEN!!! }

The blame analysis should now be complete.

As always, thank you for bringing this to our attention, and being persistent.

MFM

Who is online

Users browsing this forum: No registered users and 3 guests

About DataCAD Forum

The DataCAD Forum is a FREE online community we provide to enhance your experience with DataCAD.

We hope you'll visit often to get answers, share ideas, and interact with other DataCAD users around the world.

DataCAD

Software for AEC Professionals Since 1984