dll Delphi-to-C: передача массивов

Я использую Delphi для загрузки dll (которую я создал в Delphi XE-3) для взаимодействия с некоторым кодом C. Моя проблема заключается в том, чтобы выяснить, почему мои массивы не передаются функциям c - только они этого не делают. Файл delphi (упрощенный) выглядит так:

program CallcCode
uses
  SysUtils, Windows, 
  DLLUnit in 'DLLUnit.pas'; // Header conversion

var
  DLLHandle: cardinal;
  n: Integer;
  A: TArray<Integer>;
  result1: Integer; 

begin
  // Initialize each Array
  SetLength(A,n);
  A[0] = ...;

  // Load the DLL (and confirm its loaded)
  DLLhandle := LoadLibrary('dllname.dll');
    if DLLhandle <> 0 then
      begin
       result1 := dll_func1(n,A); // A and B are not passed correctly
    end
  FreeLibrary(DLLHandle);
end.

Я успешно «проследил» dll_func1 в первый раз, войдя в DLLUnit, который имеет:

const
  nameofDLL = 'dllname';
function dll_func1(n: Integer; A: TArray<Integer>): Integer; cdecl; external nameofDLL;

Снова «трассируя в», я прихожу к файлу c, который по-прежнему имеет правильные значения n и DLLdefs, но A (под заголовком «Локальные переменные») стал:

[-]  A       :(Aplha-Numeric)
    ..[0]    0 (0x00000000)

Я знаю, что, по крайней мере, я обращаюсь к DLL (надеюсь) правильно, потому что другие вызовы функций работают должным образом, и я могу без проблем отследить файл dll_func1.c. Я попытался изменить функцию на

function dll_func1(n: Integer; A: PInteger): Integer; cdecl; external nameofDLL;
...
result1 := dll_func1(n,PInteger(A))

or

function dll_func1(n: Integer; A: PInteger): Integer; cdecl; external nameofDLL;
...
result1 := dll_func1(n,@A[0])

(используя как TArray, так и массив Integer или A), но изменений нет, что заставляет меня поверить, что это связано с проблемой, которую я не вижу. Все это компилируется и запускается, но результат1 неверен из-за сбоев TArray. Любые идеи о том, что происходит не так?

EDIT Функция в C как:

int dll_func1(int n, int A [])

person Kendra Lynne    schedule 21.06.2013    source источник
comment
Вам просто нужно сделать то, что я сказал вам в моем ответе на ваш предыдущий вопрос. Следите очень внимательно за тем, что я написал. То, что у вас здесь, ни на что не похоже. Ну и вторая половина вопроса. Я подозреваю, что структура передана неправильно. Почему бы вам не написать функцию C, которая принимает один массив и пытается передать его. Тогда станьте более амбициозным.   -  person David Heffernan    schedule 21.06.2013
comment
Я написал PInteger в вызовах функций (файл модуля), определил их как TArray<Integer> в файле программы (где я их инициализировал) и использовал solve(n, PInteger(Ap), PInteger(Ax)); только массивы не передавались в функцию. Что вы подразумеваете под структурой, переданной неправильно?   -  person Kendra Lynne    schedule 21.06.2013
comment
Нет. PInteger — это предопределенный тип. Не переопределяйте его на что-то другое. Удалите все свои декларации. Он объявлен как PInteger = ^Integer   -  person David Heffernan    schedule 21.06.2013
comment
О, это не то, что я имел в виду, добавляя Pinteger, я имел в виду, что в объявлениях функций Ap и Ai были установлены на тип PInteger.   -  person Kendra Lynne    schedule 21.06.2013
comment
В любом случае, вам нужно научиться решать проблемы. В данный момент вы пытаетесь решить, как передать массив из Delphi в C. Требуется функция с одним параметром. Даже не думайте о чем-то более сложном, пока полностью не поймете это. Разбейте проблему на мелкие части.   -  person David Heffernan    schedule 21.06.2013
comment
Отредактировал вопрос. Я пытаюсь выяснить, какая ошибка приведет к невозможности передачи массивов, в то время как все остальное. Я пытался читать ссылки в течение последних нескольких дней, но у большинства людей были проблемы, которые были решены с помощью вашего ответа на мой предыдущий вопрос, а другим пришлось использовать ShareMem (но я не использую строки). Я просто хочу получить некоторое представление о том, что не так с моим интерфейсом. Что касается массива C, который его получает, это правильно, это не потому, что первое значение A не должно быть нулевым. Внесет другие изменения.   -  person Kendra Lynne    schedule 21.06.2013
comment
Следующий шаг — удалить все следы TArray<T> на границе интерфейса. Не могли бы вы сделать это для меня? Мы также подошли к этапу, когда, я думаю, вам нужно показать полный код. Сократите его до минимума и покажите оба кода. Делфи и С.   -  person David Heffernan    schedule 21.06.2013
comment
Я знаю, что это должно быть только одно значение, это не то, о чем я беспокоюсь. Я не пытался сказать, что ваша передача параметров была неправильной; если вы так подумали, то извините. Я уберу TArrays из вопроса, их больше нет в моем коде. Это был просто пример изменения, который я сделал для тестирования.   -  person Kendra Lynne    schedule 21.06.2013
comment
Если вы можете сократить все это, передать параметр массива как PInteger и показать полный код для обеих сторон интерфейса (используя копирование и вставку, чтобы это был код, который вы фактически запускаете), это будет очень легко и просто исправить, Я совершенно уверен.   -  person David Heffernan    schedule 21.06.2013
comment
Проблема с массивом решена, кое-что вырезал/переписал/поменял местами. Есть некоторые другие проблемы (result1 по-прежнему неверен), но, поскольку все проходит нормально, я просто буду отлаживать функцию за функцией отсюда. Я очень, очень ценю то, что терплю меня, большое вам спасибо (и извините). Я использую не функции CSparse, а один из других его алгоритмов. Я, вероятно, собираюсь удалить этот вопрос, он как бы обвел ответ на другие вопросы SO (включая мой предыдущий).   -  person Kendra Lynne    schedule 22.06.2013
comment
Вы не можете удалить вопрос, на который есть ответ с плюсом.   -  person David Heffernan    schedule 22.06.2013
comment
Отмеченный. Еще раз спасибо, приму.   -  person Kendra Lynne    schedule 22.06.2013


Ответы (1)


Ваш вопрос содержит два объявления Delphi для внешней функции. Один из них использует TArray<T> в качестве параметра. Это совершенно неправильно. Не делай этого. Вы не можете использовать динамический массив Delphi в качестве типа взаимодействия. Причина в том, что TArray<T> — это сложный управляемый тип, который может быть создан и использован только кодом Delphi.

Вам нужно сделать то, что я делаю ниже, и как я объяснил в своем ответе на ваш предыдущий вопрос, и объявить параметр массива как указатель на тип элемента. Например, PInteger, PDouble и т. д.

Здесь довольно много путаницы и ненужной сложности. Вам нужен простейший возможный пример, показывающий, как передать массив из вашего кода Delphi в код C.

Вот.

Код C

//testarray.c

void printDouble(double d); // linker will resolve this from the Delphi code

void test(double *arr, int count)
{
    int i;
    for (i=0; i<count; i++)
    {
        printDouble(arr[i]);
    }
}

Код Delphi

program DelphiToC;

{$APPTYPE CONSOLE}

uses
  Crtl;

procedure _printDouble(d: Double); cdecl;
begin
  Writeln(d);
end;

procedure test(arr: PDouble; count: Integer); cdecl; external name '_test';

{$L testarray.obj}

var
  arr: TArray<Double>;

begin
  arr := TArray<Double>.Create(1.0, 2.0, 3.0, 42.0, 666.0);
  test(PDouble(arr), Length(arr));
  Readln;
end.

Скомпилируйте код C, используя, например, компилятор Borland C следующим образом:

bcc32 -c testarray.c

И вывод:

 1.00000000000000E+0000
 2.00000000000000E+0000
 3.00000000000000E+0000
 4.20000000000000E+0001
 6.66000000000000E+0002

Обратите внимание, что я связался с кодом C статически, потому что это было проще для меня. Ничего особенного не изменится, если вы поместите код C в DLL.

Вывод таков, что код, который я дал вам в своем ответе на ваш предыдущий и который я повторяю здесь, является правильным. Этот подход успешно передает массив из кода Delphi в C. Похоже, ваша диагностика и отладка ошибочны.

Вы проверяете только A[0], поэтому неудивительно, что вы видите только одно значение. Если бы вы только посмотрели на A[1], A[2], ..., A[n-1], вы бы увидели, что все значения передаются правильно. Или, возможно, ваша отладка проводилась по ошибочному объявлению внешней функции, которая использовала TArray<T> в качестве параметра.

person David Heffernan    schedule 21.06.2013
comment
Я знаю, что на моей стороне что-то не так, и опробование различных способов передачи массивов в коде Delphi показало мне, что проблема не в самом массиве (поскольку ни один из них не работает). Проблема в том, что я новичок в dll и интерфейсах и не знаю, что тестировать дальше, поэтому я спросил о некоторых других проблемах, которые я мог бы устранить. Минимальные примеры (и этот, и тот, который я сделал) работают (на самом деле, неудивительно). Что касается структуры, то в вопросе была опечатка (я исправил); это всегда работало в моем коде, это все еще только массивы. - person Kendra Lynne; 21.06.2013
comment
Итак, что вы ищете здесь? Я уже дважды показал вам, как передавать массив. Я предполагаю здесь, но я предполагаю, что ваш массив передается нормально, но вы не знаете, как отобразить его на стороне C забора. Вы забыли показать сторону C вашего интерфейса. Какого типа параметры вашего массива? Они int*. Естественно, если вы посмотрите, скажем, на *A, то увидите только одно значение. А как же A[0], A[1] и т.д. Опять же, пожалуйста, уберите сложность. Код в вашем вопросе должен работать с одним массивом. Избавьтесь от структуры, она только отвлекает. Упрощать. - person David Heffernan; 21.06.2013