Using dynamic array of TPoint for PolyLine procedure? lazarus / 39-Graphics ---------------------------------------------------------------------- JoshM · 2013-12-13 19:33 Hi guys So I'm looking to join a series of points. The problem is, I don't know how many points. Whilst I can get my program working using a standard array (eg setting a huge array size such as 1000 to cater), using a dynamic array just feels a lot cleaner. It's more for personal satisfaction really. The only thing to consider is that the PolyLine function will be placed inside of the TNewPageControl "PaintWindow" method. The reason I'm doing this is because I need a way of plotting a line on 'top' of a page control (the Canvas is hidden beneath the PageControl). If you'd like to understand why I have to do this, you can read my original question here & the great answers I got (http://forum.lazarus.freepascal.org/index.php/topic,22130.0.html). The code contributes to a larger project, but I've just created a really simple, stripped-down version to try & get this working. Here's the code that works perfectly using a standard array. Code: [Select] unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls,   Graphics, Dialogs, Buttons, ExtCtrls, ComCtrls, StdCtrls, types, wincrt, math,   Windows; type TNewPageControl=class(TPageControl)   protected     procedure PaintWindow(DC: HDC); override;   end;   { TForm1 }   TForm1 = class(TForm)     procedure FormCreate(Sender: TObject);   private        NewPageControl: TNewPageControl;   public     { public declarations }   end; var   Form1: TForm1;   Num : integer;   DC : HDC;   TabSheet1 : TTabSheet;   Points : Array[0..10] of Tpoint; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin    Randomize;   NewPageControl := TNewPageControl.Create(Self);   NewPageControl.Left := 0;   NewPageControl.Top := 0;   NewPageControl.Height := Form1.Height;   NewPageControl.Width := Form1.Width;   NewPageControl.Parent:=Self;   TabSheet1 := TTabSheet.Create(Self);   TabSheet1.Caption := '1';   TabSheet1.PageControl := NewPageControl;   TabSheet1.Parent := NewPageControl;   NewPageControl.ActivePage := TabSheet1;   For Num := 0 to 10 do begin     Points[Num].x := Random(Form1.Width);     Points[Num].y := Random(Form1.Height);   end; end; procedure TNewPageControl.PaintWindow(DC: HDC); begin   inherited PaintWindow(DC);        Polyline(DC, Points, 11);   end; end. In reality though, I'll be drawing in excess of 500 lines, which is why a dynamic array would be nice. If I simple change the declaration and loop the following, no lines are output on screen: Code: [Select]Points : Array of TPoint; ... ... ... For Num := 0 to 10 do begin     SetLength(Points, Num + 1);     Points[Num].x := Random(Form1.Width);     Points[Num].y := Random(Form1.Height);   end; Another thing to note is that the Canvas.Polyline function takes up to 3 parameters: Array of TPoint, starting index of the array & the number of points to plot; the PolyLine function takes different parameters: function Polyline(DC: HDC; var Points; Count: Integer): BOOL; external 'gdi32' name 'Polyline';  Does anyone know how I can fix this? Could this be an error, or more likely am I just missing the 'point'? ---------------------------------------------------------------------- engkin · 2013-12-13 21:29 Putting SetLength inside the for loop does not seem right to me. I'd move it outside the loop, because you already know the highest value used in this loop (10): Code: [Select]var   Points : Array of TPoint; ...   SetLength(Points, 10 + 1);   For Num := 0 to 10 do begin     Points[Num].x := Random(Form1.Width);     Points[Num].y := Random(Form1.Height);   end; When you pass your points in a dynamic array do use the index Points[0]: Code: [Select]procedure TNewPageControl.PaintWindow(DC: HDC); begin   inherited PaintWindow(DC);   Polyline(DC, Points[0], 11); end; Notice that you can start from a different index: Code: [Select]  Polyline(DC, Points[5], 11-6); If you don't know the number of points ahead of time: Code: [Select]var   MaxNum, Num: Integer; ...   Num := 0;   MaxNum := 0;   while NotFinished do   begin     if Num>=MaxNum then     begin       MaxNum := MaxNum + 100;       SetLength(Points, MaxNum);     end;     ...     inc(Num);   end;   SetLength(Points, Num); ---------------------------------------------------------------------- howardpc · 2013-12-13 22:28 Quote from: JoshM on December 13, 2013, 08:33:39 pmAnother thing to note is that the Canvas.Polyline function takes up to 3 parameters: Array of TPoint, starting index of the array & the number of points to plot; the PolyLine function takes different parameters: function Polyline(DC: HDC; var Points; Count: Integer): BOOL; external 'gdi32' name 'Polyline';  Does anyone know how I can fix this? Could this be an error, or more likely am I just missing the 'point'? You're calling a function in an external Windows dll (written in C) which knows nothing about dynamic arrays, managed by your Pascal program completely outside the dll. You cannot pass Pascal-specific language features (dynamic arrays, classes etc.) into C-based dlls, which require strict adherence to parameter-passing rules for the data to cross the module boundary correctly. The dll you want to use shares no memory space with your program, and has no access to your program's heap (where a dynamic array would be living). AFAIK, if you want to use WinAPI calls you are restricted to using a static array. ---------------------------------------------------------------------- engkin · 2013-12-13 23:00 Quote from: howardpc on December 13, 2013, 11:28:27 pmYou're calling a function in an external Windows dll (written in C) That's the point of using DLLs. Quote from: howardpc on December 13, 2013, 11:28:27 pm..which knows nothing about dynamic arrays, managed by your Pascal program completely outside the dll. It does not need to know anything about dynamic arrays. As long as you pass a pointer to a record compatible with its design it would react as designed and expected. Quote from: howardpc on December 13, 2013, 11:28:27 pmYou cannot pass Pascal-specific language features (dynamic arrays, classes etc.) into C-based dlls, which require strict adherence to parameter-passing rules for the data to cross the module boundary correctly. Adherence to parameter-passing rules should be observed always not just when you deal with a DLL written in C language. Quote from: howardpc on December 13, 2013, 11:28:27 pmThe dll you want to use shares no memory space with your program, and has no access to your program's heap (where a dynamic array would be living). That's not true. When you import a function from this DLL it becomes part of your application and has access to whatever your application has access to. Quote from: howardpc on December 13, 2013, 11:28:27 pmAFAIK, if you want to use WinAPI calls you are restricted to using a static array. Again, not true. ---------------------------------------------------------------------- engkin · 2013-12-13 23:21 Thanks to Howardpc, without his post I wouldn't notice this mistake in the OP's code: Code: [Select]function Polyline(DC: HDC; var Points; Count: Integer): BOOL; external 'gdi32' name 'Polyline'; It's missing stdcall. It should be: Code: [Select]function Polyline(DC: HDC; var Points; Count: Integer): BOOL; stdcall; external 'gdi32' name 'Polyline';  Edit: An example: Code: [Select]type  BOOL = longbool;  HDC = System.THandle; function Polyline(DC: HDC; var Points; Count: Integer): BOOL; stdcall; external 'gdi32' name 'Polyline'; procedure TForm1.Button1Click(Sender: TObject); var   Points: Array of TPoint;   NumOfPoints, i: integer;   coff: Extended; begin   NumOfPoints := 20;   SetLength(Points, NumOfPoints);   coff := 2*PI/NumOfPoints;   for i:=Low(Points) to High(Points) do   begin     Points[i].x := Width div 2  + Round(Width *sin(coff*i)/2);     Points[i].y := Height div 2 + Round(Height*cos(coff*i)/2);   end;   Polyline(Canvas.Handle, Points[0], NumOfPoints);   SetLength(Points, 0); end;