Sonjli 6 Posted October 28, 2019 Hi guys, I hope this is the right topic to place my doubts. I use a DLL written in C and it need some work with pointers. I admit I am not very prepared with pointers (scholastic remembrances), so I hope my question to be clear. I have this structure: PODBALMMSG2 = ^ODBALMMSG2; ODBALMMSG2 = packed record alm_no: LongInt; atype: SmallInt; axis: SmallInt; dummy: SmallInt; msg_len: SmallInt; alm_msg: array [0 .. 63] of AnsiChar; end; And this is the function I use to populate this structure and read it: procedure TRdAlmMsgActionA.InternalRdAlmMsg; var iAlarm: Integer; LErrorCounter: Integer; rn: SmallInt; popmsg: PODBALMMSG2; begin FPath := ''; LErrorCounter := 0; // A random number the DLL need to have any initial value rn := 50; try // A pre-filled number with "max paths" (FConnection.MachinePaths) to read from DLL for var J := 1 to FConnection.MachinePaths do begin // A DLL function to loop between "Paths" until "FConnection.MachinePaths" FConnection.ErrHandle := cnc_setpath(FConnection.ConnHandle, J); if FConnection.ErrHandle <> EW_OK then begin Inc(LErrorCounter); Continue; end; // Here I need help: is this algorithm ok? new(popmsg); try // The DLL fill "popmsg" with an array of "PODBALMMSG2" with length "rn" FConnection.ErrHandle := cnc_rdalmmsg2(FConnection.ConnHandle, -1, rn, popmsg); if FConnection.ErrHandle <> EW_OK then begin Inc(LErrorCounter); Continue; end; // Loop for "popmsg" of length "rn" for var I := 0 to rn - 1 do begin // Here I do reading stuff with "popmsg^.alm_no", "popmsg^.aType", etc. // Incremet pointer to read next array value Inc(popmsg); end; finally // Bring the pointer at the start of the array Dec(popmsg, rn); // Free the pointer Dispose(popmsg); end; end; finally cnc_setpath(FConnection.ConnHandle, 0); end; end; I have so many doubts about this: - Have the popmsg pointer to be brought at the start before dispose? - Is the loop right? - Is the "new()" function right to allocate the pointer? - And so on... Any help? Thanks in advance. Eddy Share this post Link to post
Cristian Peța 103 Posted October 28, 2019 First you need to know the maximum length of "rn" and allocate that maximum from start. Something like this: popmsg: array[0..100] of ODBALMMSG2; and call it like this: FConnection.ErrHandle := cnc_rdalmmsg2(FConnection.ConnHandle, -1, rn, @popmsg[0]); This way you don't need pointers. Share this post Link to post
Sonjli 6 Posted October 28, 2019 (edited) Hi Cristian, thanks for your answer. How do I allocate popmsg? How do I dispose the array? How do I read an element? @popmsg[4].alm_no or popmsg[4]^.alm_no? Eddy Edited October 28, 2019 by Sonjli Share this post Link to post
Anders Melander 1782 Posted October 28, 2019 1 hour ago, Sonjli said: How do I [...] The array is static and is allocated on the stack. You could also use a dynamic array to have it allocated on the heap. Static is faster but stack space is limited. Regardless the array is automatically deallocated when it goes out of scope. Anyway: procedure TRdAlmMsgActionA.InternalRdAlmMsg; const MaxMsgs = 50; var rn: SmallInt; popmsgs: array[0..MaxMsgs-1] of ODBALMMSG2; i: integer; begin ... while (True) fo begin rn := MaxMsgs; // rn on entry contains max items the buffer can hold // rn on exit contains number of items now in buffer // See: https://www.inventcom.net/fanuc-focas-library/misc/cnc_rdalmmsg2 FConnection.ErrHandle := cnc_rdalmmsg2(FConnection.ConnHandle, -1, rn, @popmsgs[0]); if (rn = 0) then break; // No more available for i := 0 to rn-1 do begin popmsgs[i].whatever... end; end; end; Share this post Link to post
Sonjli 6 Posted October 28, 2019 Thank you Anders, really precious. As you see I use the FOCAS library to connect some CNC and sometimes there are some traps... Are you skilled with FOCAS? May I ask you any questions sometimes if I am in danger? 😅 Share this post Link to post
Anders Melander 1782 Posted October 28, 2019 4 minutes ago, Sonjli said: Are you skilled with FOCAS? Never used it. I just Googled "cnc_rdalmmsg2" to find out if the function provided some means of determining the required buffer size. Like it is common with WinAPI functions (ERROR_INSUFFICIENT_BUFFER). Share this post Link to post
David Heffernan 2345 Posted October 28, 2019 The API will be documented. It's essential that you use that documentation. Share this post Link to post
Sonjli 6 Posted October 29, 2019 The API is documented and I used it for years. The my only problem is about pointers... I know, don't say a word 😉 Do you have any Pascal related documentation\manual about pointers? Thanks everybody. Eddy Share this post Link to post
Cristian Peța 103 Posted October 29, 2019 You need first to understand what a pointer is and how things like pointers and other types are stored into memory. Maybe diving a little in assembler will help you to understand what is under the hood. I can't imagine a good programmer that don't know a little how CPU works or how things are stored into memory. Share this post Link to post
David Heffernan 2345 Posted October 29, 2019 11 minutes ago, Cristian Peța said: Maybe diving a little in assembler will help you to understand what is under the hood. This has to be, in my opinion, one of the worst pieces of advice I have ever come across. Share this post Link to post
David Heffernan 2345 Posted October 29, 2019 41 minutes ago, Sonjli said: Do you have any Pascal related documentation\manual about pointers? Type delphi pointers into a search engine and start there. I'm sure there will be a mix of hits. Some better than others. Read the top 10, form a view on which are good and which are not. Reread the good ones. 1 Share this post Link to post
Cristian Peța 103 Posted October 29, 2019 (edited) 12 minutes ago, David Heffernan said: This has to be, in my opinion, one of the worst pieces of advice I have ever come across. I started with Basic. The next was Z80 assembler. But assembler opened my eyes and is a base of understanding how things works. P.S. I can't imagine that someone can do a good thing in Spring4D for example without understanding CPU and memory. Also if someone does not have this knowledge working with C or C++ is a waste of time in my opinion. Then better Java. Edited October 29, 2019 by Cristian Peța Share this post Link to post
Lars Fosdal 1792 Posted October 29, 2019 Edit: I rewrote this on my blog: https://larsfosdal.blog/2019/10/31/demystifying-pointers-in-delphi/ Having a general understanding of how memory and addressing works, helps a bit for pointers. Learning assembler gives you that knowledge, but there are simpler ways to think about it. A pointer is a memory location that contains the address to the actual data you want Think of a street with houses. Make a list of the houses and their addresses. This is a list of pointers. Each pointer leads to the actual house it refers to. As you move through the list and follow each pointer, you can visit each house. Street of houses (i.e. your blocks of data, 1Kb each) 10k 11k 12k 13k 14K +------------+------------+------------+------------+------------+ |Apple |Pear | |Banana |Orange | | H1 | H2 | | H3 | H4 | | | | | | | +------------+------------+------------+------------+------------+ Your list of addresses (aka 4 byte pointers) is stored at memory address 100k var ptrlist: array[0..3] of pointer; assuming the list has been initialized with the correct addresses ptrlist[0] 100k contains 10k ptrlist[1] 100k+4 contains 11k ptrlist[2] 100k+8 contains 13k ptrlist[3] 100k+12 contains 14k for var ix := 0 to Length(ptrlist) - 1 do begin here, ptrlist[ix] = 10k,11k,13k,14k, and ptrlist[ix]^ = whatever value that is stored in the respective house the pointer addresses f.x. ptrlist[1] contains 11k (and that value is stored at 100k+4, and ptrlist[1]^ points to 'Pear', i.e. whatever is stored from address 11k Why the empty house? To exemplify that your list of pointers may be a consecutive array or linked list, but the data each pointer points to does not necessarily need to be consecutive. Now, if you address ptrlist[4] - you are out of bounds on the pointer list, and if you are so "lucky" that the address @ptrlist[4] (which is 100k+16) is not inaccesible, then ptrlist[4]^ will point you to whatever random value that pointer contains,, and most likely give you an access violation, or for sure - point you to data you are not meant to visit. 3 1 Share this post Link to post
PeterBelow 238 Posted October 31, 2019 On 10/29/2019 at 8:41 AM, Sonjli said: The API is documented and I used it for years. The my only problem is about pointers... I know, don't say a word 😉 Do you have any Pascal related documentation\manual about pointers? Thanks everybody. Eddy http://rvelthuis.de/articles/articles-pointers.html 3 1 Share this post Link to post
pcplayer99 11 Posted October 31, 2019 PODBALMMSG2 = ^ODBALMMSG2; ODBALMMSG2 = packed record alm_no: LongInt; atype: SmallInt; axis: SmallInt; dummy: SmallInt; msg_len: SmallInt; alm_msg: array [0 .. 63] of AnsiChar; end; In Delphi,if you has code like this: procedure DoSometiing(xxxxx: xx); var MyR: ODBALMMSG2 begin ////do something... end; Here, you got a record MyR, its memory is prepared statically. It is a instance, you can use it directly. And the pointer of this instance is: @MyR. If you has code like this: procedure DoSometing(xxx: xxx); var MyR_P: PODBALMMSG2 begin /// Here, MyR_P is a pointer that type is PODBALMMSG2 but it is a pointer, you can just use a normal pointer type instead PODBALMMSG2. /// Here, MyR_P is just a variable, there is no instance, no memory that store data. If you want use it, you must allocate memory for it. And, you must free the memory when you want to give up it. MyR_P := GetMem(SizeOf(ODBALMMSG2)); try do something... finally FreeMem(MyR_P); end; /// or, you can use this function: New(MyR_P); try Do something.... finally Dispose(MyR_P); end; end; 1 Share this post Link to post