Передача двоичных данных из Qt / C ++ DLL в хост-приложение Delphi

В программе я хочу использовать QImage.bits () в Delphi. Итак, в Qt я была создана dll. Исходный код dll, указанный ниже:

test.h:

  #ifndef TEST_H
    #define TEST_H

    #include "test_global.h"


    extern "C"{
    TESTSHARED_EXPORT uchar* testFunc();
    }


    #endif // TEST_H

test.cpp:

 #include "test.h"
#include <QtGui>

QImage image;

uchar* testFunc(){
    image.load("c:\\1.png","PNG");
    return (uchar*)image.constBits();
}

а на стороне Delphi я использую этот код для использования Qt dll:

    function testFunc(): PByteArray; external 'test.dll';
// ...

procedure TForm3.Button1Click(Sender: TObject);
var
  bStream: TBytesStream;
  P: PByteArray;
  Size: Cardinal;
begin
  P := testFunc;
  Size := Length(PAnsiChar(P)); // AnsiChar = 1 Byte
  bStream := TBytesStream.Create();
  try
    bStream.Write(P[0], Size); // Works Fine (^_^)
    bStream.Position := 0;
    bStream.SaveToFile('c:\scr.txt');
  finally
    bStream.Free;
  end;
end;

когда я вызываю функцию dll, никакие данные не возвращаются! можешь помочь мне?

Обновление 1: В реальной ситуации моя функция Qt очень сложна, и я не могу написать ее на Delphi по многим причинам. Фактически исходная функция снимала снимок экрана с устройства и работала на нем в основной памяти. и в результате я хочу отправить байты этого изображения в Delphi для отображения его в TImage, но не сохранять его на жестком диске и подобных воспоминаниях. и в этом разделе я просто создал простую аналогичную функцию для простой отладки и тестирования. Можно ли мне помочь, написав действительно простой код для решения этой проблемы? Большое вам спасибо. (-_-)


person Shaahin Ashayeri    schedule 07.09.2012    source источник
comment
Здесь нет необходимости в Qt. Вы можете читать файлы PNG из Delphi.   -  person David Heffernan    schedule 07.09.2012
comment
зачем этот беспорядок с TByteStream? Почему бы не использовать TFileStream напрямую?   -  person Arioch 'The    schedule 07.09.2012
comment
Ответ Ариоху Вопрос: но в реальной ситуации не существует никакого файла изображения png и изображения, возвращенного с устройства. Итак, для уменьшения времени отклика я не могу использовать какой-либо файл, и мое приложение должно использовать основную память для загрузки данных изображения и их отображения. моя функция Qt очень сложна, и я не могу преобразовать ее в исходный код delphi.   -  person Shaahin Ashayeri    schedule 07.09.2012
comment
Ответ Дэвиду Хеффернану: Я знаю это. но этот исходный код - простая симуляция моей реальной проблемы.   -  person Shaahin Ashayeri    schedule 07.09.2012
comment
Хорошо, тогда вам просто нужно правильно настроить соглашение о вызовах и сопоставить uchar* с PByte. Вот и все.   -  person David Heffernan    schedule 07.09.2012
comment
@Shuhin - при ответе SO использует соглашение твиттера. Используйте @ перед именем. Это уведомит вашего корреспондента о новом непрочитанном ответе.   -  person Arioch 'The    schedule 07.09.2012
comment
Откровенно говоря, если время отклика так важно, почему бы не сделать монолитную программу, полностью на Delphi или полностью на Qt?   -  person Arioch 'The    schedule 07.09.2012
comment
@David - недостаточно: он также должен передать длину блоба данных, а не только указатель на его голову :-)   -  person Arioch 'The    schedule 07.09.2012


Ответы (2)


Более простым было бы использование PByte aka ^ Byte, а не PByteArray. Хотя TByteArray является статическим типом, использование массивов по-прежнему увеличивает риск случайной опечатки, как при использовании динамических массивов.


Не используйте PChar - он остановится на 1-м нулевом байте. Картинка не является строкой, она может содержать сотни нулей.

Вы должны отправить длину как отдельную переменную / функцию. int QImage :: byteCount () const http://qt-project.org/doc/qt-4.8/qimage.html#byteCount


Отредактируйте свой вопрос, пожалуйста. Вы спрашиваете о битах или постоянных битах? это разные свойства!


Также:

Вы должны узнать о «соглашениях о вызовах» в ваших компиляторах, C ++ и Pascal. Лучше бы вы могли отслеживать это на уровне Ассемблера.

попробуйте пометить эту процедуру директивой «cdecl» в коде Паскаля или, возможно, директивой «stdcall» как в коде C, так и в коде Паскаля.

Попробуйте утилиту h2pas от FreePascal для автоматической конвертации.


А теперь лучше всего - перетащите сюда Qt. Создавать мосты Qt только для чтения файлов PNG очень странно и хрупко. Существует ряд собственных библиотек Delphi с поддержкой PNG. Несколько примеров:

person Arioch 'The    schedule 07.09.2012
comment
Ваше понимание PByteArray неверно. Это не указатель на запись. Это указатель на обычный массив. В объявлении говорится, что массив, на который указывает указатель, содержит элементы MaxInt, но на самом деле это просто для удовлетворения кода проверки диапазона, который может выдать компилятор, что было бы бесполезно для такого типа указателя на массив. На практике массив может иметь любую длину. В вопросе нет динамических массивов. - person Rob Kennedy; 07.09.2012
comment
Я думаю, что мой ответ лучше, чем ответ Ариоха. но я не могу окончательно проверить свой ответ. Поэтому я проверил пост Ариоха в качестве окончательного ответа. - person Shaahin Ashayeri; 07.09.2012
comment
@ Роб, ну тогда память меня обманула. Да, это не динамический массив. Но тогда почему не возвращаются никакие данные? Это должно означать, что самый 1-й байт уже равен нулю? Даже если мы пропустим заголовок PNG - я действительно ничего не знаю о Qt, а topicstarter смешивает два разных свойства - вероятность того, что 1-й пиксель будет ровно 0, кажется не большой. - person Arioch 'The; 07.09.2012
comment
@Shuhin_Ashayeri Вы сможете проверить свой ответ через пару дней. AFAIR, что следует написать о ваших попытках. Ваш ответ показывает, что делать, но не говорит ни о том, что изменилось, ни почему. Представьте себе человека, который очень мало знает C ++ и Паскаль. Сможет ли он проверить весь код, который вы почти полностью переделали, и понять, какие изменения были важны и почему? Я сомневаюсь. Откровенно говоря, наши ответы комплиментарны. Если бы вы выделили и объяснили изменения в своем коде, это было бы наиболее полезным ответом. - person Arioch 'The; 07.09.2012

Задача решена. (^_^)

Для его решения:

На стороне Qt:

test.h:

#ifndef TEST_H
#define TEST_H

#include "test_global.h"


extern "C"{
TESTSHARED_EXPORT char* testFunc(int &a);
}


#endif // TEST_H

test.cpp:

#include "test.h"
#include <QtGui>

QImage image;
QByteArray ba;


char* testFunc(int &a){
    image.load("c:\\2.png","PNG");
    QBuffer buffer(&ba);
    buffer.open(QIODevice::WriteOnly);
    image.save(&buffer,"PNG");
    a = ba.size();
    return ba.data();
}

На стороне Дельфи:

function testFunc(var aByteCount: DWORD): PByte;cdecl external 'test.dll';

// ...
var
  bStream: TBytesStream;
  Size: DWORD;
procedure TForm3.Button1Click(Sender: TObject);
var
  P: PByte;
  s: TStringList;
begin
  Caption := '0';
  P := testFunc(Size);
  bStream := TBytesStream.Create();
  try
    bStream.Write(P[0], Size);
    bStream.Position := 0;
    bStream.SaveToFile('c:\my.png');
  finally
    Caption := IntToStr(Size);
  end;
end;

Еще раз спасибо за "Arioch The" и "David Heffernan". (^_^)

person Shaahin Ashayeri    schedule 07.09.2012
comment
@ Дэвид, его ответ имеет ценность как рабочий код. Хотя я согласен с тем, что по духу SO такой образец кода лучше было бы добавить как Обновление 2 в исходный вопрос, а не как независимый ответ. - person Arioch 'The; 07.09.2012
comment
@ Arioch'The Ясно, что Шухин еще не совсем понял, как работает SO. Я пытаюсь услужливо научить его / ее принимать ответы. - person David Heffernan; 07.09.2012
comment
Нет, @Arioch, это не обновление вопроса. Это не часть вопроса. Это место в ответе. Поскольку вы не поместили рабочий код для решения проблемы в свой ответ, Шухин поместил его в свой собственный ответ. Ваш ответ был бы лучше, если бы он продемонстрировал это, поскольку единственная реальная проблема в исходном коде - это метод определения длины буфера. - person Rob Kennedy; 07.09.2012
comment
@Rob, кхм, абсолютно неправильный тип данных, который может допустить AV или (при попытке редактирования на месте) повреждение памяти - это не считается для вас важной проблемой? Ну да. Ответ состоит в том, как и что. Я предпочитаю как расстаться. Меня мало интересуют обезьяны с копипастом, и я считаю, что создание собственной реализации научит вас лучше, чем копипаст. Тем не менее, я согласен, что этот образец кода тоже имеет свою ценность. Также я никогда не работал с Qt, поэтому я не могу ни тестировать код, ни полагаться на свой опыт. И отправив в качестве ответа гипотетический код, который, вероятно, работает в соответствии со здравым смыслом, нет - person Arioch 'The; 07.09.2012
comment
@Rob В коде в Q есть несоответствие соглашения о вызовах, несоответствие типа и отсутствующий параметр длины данных. Ариох получил все, даже если бы письмо могло быть более четким. - person David Heffernan; 07.09.2012
comment
Хорошо, я понял вашу точку зрения о типах данных. Скорее всего, в этом вы правы. Однако мне никакие данные не кажутся странными. - person Arioch 'The; 07.09.2012
comment
@David - см. Комментарий Роба к моему ответу. TByteArray - статический, а не динамический. Соглашение о вызовах - это ПЛОХАЯ ошибка, но, учитывая функцию без параметров, возвращающую int32, она должна совпадать по чистой случайности. В конце концов, Роб кажется правым. Исходный код плохой, но кроме PChar - почему не сработало? А что такое длина (PAnsiChar)? Это в основном (Length (AnsiString (PAnsiChar)) или Length (UnicodeString (PAnsiChar)) или, возможно, Length (массив [0 ..- 1] AnsiChar (PChar ^))? Я не заметил Length вместо StrLen, используемого в указателе ... стыд, стыд ... - person Arioch 'The; 07.09.2012
comment
@Arioch, Length(PAnsiChar(x)) имеет неявное преобразование в AnsiString, поэтому он эквивалентен StrLen(PAnsiChar(x)), но без выделения памяти. (Length полиморфен; он не приведет к принудительному преобразованию в UnicodeString, если предоставленный тип аргумента преобразуется в AnsiString более естественно.) - person Rob Kennedy; 08.09.2012
comment
@Rob Length является полиморфным, но указатель не имеет типа. Однажды я сделал PAnsiChar (UnicodeString) или, возможно, PWideChar (AnsiString), ожидая преобразования, и был сильно укушен. С тех пор я не доверяю автоматизации типов PChar - person Arioch 'The; 09.09.2012
comment
Да, @Arioch, Pointer бестиповый. Вот почему вы не можете позвонить Length по одному. Вы можете вызвать его на PAnsiChar и PWideChar, потому что они не лишены типа. PAnsiChar автоматически преобразуется в AnsiString; От PWideChar до WideString или UnicodeString, в зависимости от доступности и контекста. Обратные преобразования никогда не происходят неявно, а преобразования происходят только в том случае, если они относятся к одному и тому же типу символов. Преобразования между типами символов не происходят, поэтому UnicodeString никогда не преобразуется в PAnsiChar. Если вы попробуете, вы просто получите простое приведение типов, а не преобразование. - person Rob Kennedy; 14.09.2012
comment
@ Роб, не совсем типографский. Typecast даст мне указатель на StringRec. Вместо этого я получаю указатель на строку [1]. Так что некоторая часть магии компилятора все еще применяется, но только частично ... - person Arioch 'The; 17.09.2012
comment
@ Ариох, это именно то, что вам даст приведение типов. Переменная string содержит адрес. Приведите его к типу, и все, к чему вы его приведете, по-прежнему содержит тот же адрес, но интерпретируется как имеющий другой тип. Если вам нужен указатель на первый байт, выделенный диспетчером памяти, вам нужно выполнить некоторую арифметику, и тогда это больше не будет приведением типов. Если результаты до и после операции не идентичны двоично, то это не приведение типа; это конверсия. Магия компилятора не влияет на приведение типов. - person Rob Kennedy; 17.09.2012
comment
@Rob, а как тогда получить указатель на refcounter и длину? отрицательное смещение до указателя (строки)? - person Arioch 'The; 17.09.2012
comment
Как я уже сказал, @Arioch, займись арифметикой. Приведение типа (к Cardinal или Pointer) для получения базового адреса, вычитание, а затем приведение вычисленного адреса обратно к PStrRec. Если бы компилятор сам поддерживал эту операцию, это было бы классифицировано как преобразование, потому что результат не был таким же, как ввод. - person Rob Kennedy; 17.09.2012