Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>To use a dynamic array with <a href="http://www.delphibasics.co.uk/RTL.asp?Name=Move" rel="noreferrer">the <code>Move</code> procedure</a>, you need to pass the <em>first element</em> of the array. For example:</p> <pre><code>var Source: Pointer; SourceSize: Integer; Destination: array of Byte; SetLength(Destination, SourceSize); Move(Source^, Destination[0], SourceSize); </code></pre> <p>Notice also that the second parameter dereferences the pointer. That's because <code>Move</code> takes the <em>value</em> that you're copying, not a pointer to the value. You're copying the stuff that your pointer points to, so that's what you need to pass to <code>Move</code>.</p> <p>Incidentally, that same syntax works if <code>Destination</code> is a static array, too. And you're right that this is not specific to Delphi 2009. It's true all the way back to Delphi 4, which is when dynamic arrays were introduced. And <code>Move</code> has had the same strange <a href="http://www.cs.wisc.edu/~rkennedy/untyped" rel="noreferrer">untyped parameter</a> syntax forever.</p> <hr> <p>Do not allocate your own memory with <code>GetMem</code> and then type-cast to make the compiler think that what you have is a dynamic array. <strong>It's not</strong>. Dynamic arrays have reference counts and length fields that an ordinary byte buffer won't have, and since you're not in control of all the code the compiler generates to access the supposed dynamic array, there's a danger that your program will try to access the data structure's nonexistent data.</p> <p>You could make the PSP function store its data directly into a dynamic array. Here's some code to do it:</p> <pre><code>var Output: array of Byte; SetLength(Output, OutputLength.Value); if SendPSPQuery(Char(DriveLetter[1]), cbxQuery.Items.IndexOf(cbxQuery.Text), @Output[0], OutputLength.Value) = 0 then </code></pre> <p>No need to free the memory afterward; the compiler inserts code to deallocate the dynamic array when <code>Output</code> goes out of scope and there are no other references to the array. This code takes a dynamic array and passes it as though it were an ordinary buffer. This works and is safe because a dynamic array is, in effect, a subtype of a plain old buffer. The function will accept a pointer to the first element of the array and treat the pointer as a pointer to a bunch of bytes because that's exactly what it is. The function doesn't need to know that there happens to be additional stuff adjacent to those bytes that the program uses for dynamic-array bookkeeping.</p> <hr> <p>If you have your data in a buffer and you want to treat that buffer as though <em>it</em> were the array, instead of copying the data into a separate data structure, then you have two options.</p> <ol> <li><p>Declare a static-array <em>pointer</em>, and then type-cast your buffer pointer to that type. This is the classic technique, and you can see it used in code all over the place, especially code that predates Delphi 4. For example:</p> <pre><code>type PByteArray = ^TByteArray; TByteArray = array[0..0] of Byte; var ByteArray: PByteArray; ByteArray := PByteArray(Output); for i := 0 to Pred(OutputLength.Value) do begin {$R-} edtString.Text := edtString.Text + Chr(ByteArray[i]); {$R+} end; </code></pre> <p>The <code>$R</code> directives are to make sure range checking is turned off for that code since the array type is declared to have a length of 1. The array is declared with that size in part to serve as a clue that you're not really supposed to declare a variable of that type. Only use it through a pointer. On the other hand, if you know what a suitable maximum size of the data will be, you can use that size to declare the array type instead, and then you can keep range checking turned on. (If you normally keep range checking disabled, you're just asking for trouble.)</p></li> <li><p>Declare your buffer as <code>PByte</code> instead of <code>Pointer</code> and then use Delphi's new (as of Delphi 2009) <a href="http://blogs.embarcadero.com/abauer/2008/01/24/38852" rel="noreferrer">support for treating arbitrary pointer types as array pointers</a>. In previous versions, only <code>PChar</code>, <code>PAnsiChar</code>, and <code>PWideChar</code> supported this syntax. For example:</p> <pre><code>var Output: PByte; for i := 0 to Pred(OutputLength.Value) do begin edtString.Text := edtString.Text + Chr(Output[i]); end; </code></pre> <p>The <code>$POINTERMATH</code> compiler directive is not required to enable this feature for <code>PByte</code> because that type is <em>declared</em> while that directive is in effect. If you want to do C-like pointer operations with <em>other</em> pointer types, then place <code>{$POINTERMATH ON}</code> before the code that makes use of the new extended syntax.</p></li> </ol> <hr> <p>As a final note, you don't need to build up your strings one character at a time. It's wasteful in two ways. First, you're constructing lots of strings, each one just two bytes larger than the previous one. Second since you're storing the string result in an edit control, you're forcing the OS implementation of that control to allocate a bunch of new strings, too. Put your data into <em>one</em> string, and then append it all at once to your edit control:</p> <pre><code>var OutputString: AnsiString; SetString(OutputString, PAnsiChar(Buffer), OutputLength.Value); edtString.Text := edtString.Text + OutputString; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload