Чтобы использовать динамический массив с процедурой Move
, вам нужно передать первый элемент массива. Например:
var
Source: Pointer;
SourceSize: Integer;
Destination: array of Byte;
SetLength(Destination, SourceSize);
Move(Source^, Destination[0], SourceSize);
Обратите внимание, что второй параметр разыменовывает указатель. Это потому, что Move
принимает значение , которое вы копируете, а не указатель на значение. Вы копируете материал, на который указывает указатель, поэтому вам нужно передать его на Move
.
Кстати, тот же синтаксис работает, если Destination
также является статическим массивом. И вы правы, что это не относится к Delphi 2009. Это верно вплоть до Delphi 4, когда появились динамические массивы. И Move
имеет такой же странный нетипизированный параметр синтаксис навсегда.
Не выделяйте собственную память с помощью GetMem
, а затем приводите тип, чтобы заставить компилятор думать, что у вас есть динамический массив. Это не . Динамические массивы имеют счетчики ссылок и поля длины, которых не будет у обычного байтового буфера, и, поскольку вы не контролируете весь код, который генерирует компилятор для доступа к предполагаемому динамическому массиву, существует опасность, что ваша программа попытается получить доступ несуществующие данные структуры данных.
Вы можете заставить функцию PSP хранить свои данные непосредственно в динамическом массиве. Вот некоторый код, чтобы сделать это:
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
Нет необходимости освобождать память впоследствии; компилятор вставляет код для освобождения динамического массива, когда Output
выходит из области видимости, и нет других ссылок на массив. Этот код принимает динамический массив и передает его как обычный буфер. Это работает и безопасно, потому что динамический массив, по сути, является подтипом простого старого буфера. Функция примет указатель на первый элемент массива и обработает указатель как указатель на группу байтов, потому что это именно то, что есть. Функция не должна знать, что рядом с теми байтами, которые программа использует для учета динамических массивов, случается что-то дополнительное.
Если у вас есть данные в буфере и вы хотите обработать этот буфер, как если бы это был массивом, вместо того, чтобы копировать данные в отдельную структуру данных, тогда у вас есть две опции.
Объявите статический массив указатель , а затем приведите тип буфера к этому типу указателя. Это классический метод, и вы можете увидеть, что он используется повсеместно в коде, особенно в коде, предшествующем Delphi 4. Например:
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;
Директивы $R
предназначены для того, чтобы убедиться, что проверка диапазона отключена для этого кода, поскольку тип массива объявлен с длиной, равной 1. Массив объявлен с таким размером частично, чтобы служить подсказкой того, что вы ' на самом деле не предполагается объявлять переменную этого типа. Используйте его только через указатель. С другой стороны, если вы знаете, какой будет максимальный размер данных, вы можете использовать этот размер для объявления типа массива, а затем оставить проверку диапазона включенной. (Если вы обычно отключаете проверку диапазона, вы просто напрашиваетесь на неприятности.)
Объявите ваш буфер как PByte
вместо Pointer
, а затем используйте новую поддержку Delphi (начиная с Delphi 2009) для обработки произвольных типов указателей как указателей на массивы . В предыдущих версиях этот синтаксис поддерживали только PChar
, PAnsiChar
и PWideChar
. Например:
var
Output: PByte;
for i := 0 to Pred(OutputLength.Value) do begin
edtString.Text := edtString.Text + Chr(Output[i]);
end;
Директива компилятора $POINTERMATH
не требуется для включения этой функции для PByte
, поскольку этот тип объявлен , пока эта директива действует. Если вы хотите выполнять C-подобные операции с указателями с другими типами указателей, поместите {$POINTERMATH ON}
перед кодом, использующим новый расширенный синтаксис.
Как последнее замечание, вам не нужно создавать строки по одному символу за раз. Это расточительно двумя способами. Во-первых, вы создаете много строк, каждая из которых на два байта больше, чем предыдущая. Во-вторых, поскольку вы сохраняете строковый результат в элементе управления edit, вы заставляете реализацию этого элемента в ОС также выделять кучу новых строк. Поместите ваши данные в одну строку, а затем добавьте их все сразу к вашему редактору:
var
OutputString: AnsiString;
SetString(OutputString, PAnsiChar(Buffer), OutputLength.Value);
edtString.Text := edtString.Text + OutputString;