Как интерпретировать массивы, переданные из VBscript в приложение Delphi COM-сервер - PullRequest
3 голосов
/ 14 января 2020

Я пытаюсь передать массив байтов из VBscript в мое windows Delphi приложение и не могу найти правильный синтаксис для интерпретации переданных данных.

Требование довольно простое как показывает приведенный ниже фрагмент VBscript

Dim inst,arr(5)

Sub Main
  set inst=instruments.Find("EP1")
  arr(0) = 0
  arr(1) = 1
  arr(2) = 2
  arr(3) = 3
  arr(4) = 4
  inst.writebytes arr,5
end Sub

Я могу заставить сервер принять олевариант, переданный сценарием, но данные кажутся искаженными, мой пример кода сервера показан ниже и основан на вопросе Stackoverflow здесь Как использовать вариантные массивы в Delphi

procedure TInstrument.WriteBytes(Data: OleVariant; Length: Integer);
var i,n:integer; Pdat:Pbyte; Adata:PvarArray;
begin
  if VarIsArray(data) then
  begin
    n:=TVarData(Data).VArray^.Bounds[0].ElementCount;
    Adata:= VarArrayLock(Data);
    Getmem(Pdat,length);
    try
      for i:=0 to length-1 do
        Pdat[i]:=integerArray(Adata.data^)[i];
      Finstrument.WriteBytes(Pdat,Length);
    finally
      freemem(Pdat)
    end;
  end;
end;

Таким образом, идея состоит в том, чтобы принять целые числа, переданные сценарием, преобразовать их в локальное представление данных (массив байтов) и затем передать в моей функции использовать данные.

Я пробовал несколько разных типов данных и методов, чтобы попытаться получить некоторые неопрятные данные из варианта, но все безрезультатно.

Что такое правильный метод извлечения данных массива из переданного варианта?

Кроме того, TVarData (Data) .VArray ^ .Bounds [0] .ElementCount имеет значение ноль, с чего бы это?

1 Ответ

5 голосов
/ 14 января 2020

Массивы, созданные в VBScript,

  1. на основе нуля
  2. нетипизированные
  3. , объявленные с верхней границей (не размером, как вы предполагали; размер массива, объявленный как * 1008) * is 6)
  4. включает в себя информацию об измерениях (поэтому вам не нужно передавать ее вместе с массивом)

При использовании в COM они передаются как вариантные массивы типа varVariant (как указывает Ондрей Келле в своем комментарии). Для обработки такого массива в вашем методе вы должны утверждать, что:

  1. значение представляет собой одномерный массив
  2. каждый элемент может быть преобразован в байт

Вы можете написать вспомогательную подпрограмму для этого:

function ToBytes(const Data: Variant): TBytes;
var
  Index, LowBound, HighBound: Integer;
  ArrayData: Pointer;
begin
  if not VarIsArray(Data) then
    raise EArgumentException.Create('Variant array expected.');
  if VarArrayDimCount(Data) <> 1 then
    raise EArgumentException.Create('Single dimensional variant array expected.');
  LowBound := VarArrayLowBound(Data, 1);
  HighBound := VarArrayHighBound(Data, 1);
  SetLength(Result, HighBound - LowBound + 1);
  if TVarData(Data).VType = varArray or varByte then
  begin
    ArrayData := VarArrayLock(Data);
    try
      Move(ArrayData^, Result[0], Length(Result));
    finally
      VarArrayUnlock(Data);
    end;
  end
  else
  begin
    for Index := LowBound to HighBound do
      Result[Index - LowBound] := Data[Index];
  end;
end;

for l oop в подпрограмме будет ужасно медленной при обработке больших массивов, поэтому есть оптимизация для особого случая (вариант массива байтов) который использует Move для копирования байтов в результате. Но это никогда не произойдет с массивом VBScript. Вы могли бы рассмотреть возможность использования VB. Net или PowerShell.

Использование такой подпрограммы имеет недостаток - хранить 2 экземпляра массива в памяти - как вариантный массив и как байтовый массив. Используйте его в качестве руководства при применении к вашему случаю использования.

...