0 / 0 / 0
Регистрация: 08.11.2013
Сообщений: 20
1

Делфийская DLL и основная программа C++, передача агрументов туда-сюда

08.11.2013, 19:46. Показов 2290. Ответов 35
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Здравствуйте!

Имеется
DLL:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
library M3_78M;
 
var N: integer;
 
{$R *.res}
 
function GetValue(PM:pointer; out PN:pointer): boolean; stdcall; Export;
 var M: integer;
     MM: ^integer;
Begin
 GetValue:=false;
 
 MM:=PM;
 M:=MM^;
 N:=M+1;
 PN:=@N;
 
 GetValue:=true;
end;
 
{----------------------------------------------------------------------}
 
Exports GetValue;
 
begin
end.
Осн. прога:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
 
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormActivate(TObject *Sender)
{
 hM3_dll = LoadLibrary("M3_78M.dll");
 GetValue =(TGetValue)GetProcAddress(hM3_dll,"GetValue");
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 int Mumber,     // Входной параметр
   *AMumber,   // Указатель (адрес) входного параметра
     Number,     // Выходной параметр
   *ANumber,    // Указатель (адрес) выходного параметра
  **AANumber;  // Указатель (адрес) указателя (адреса) вых. параметра
 
// Присвоение значения входному параметру
 Mumber=StrToInt(Edit2->Text);
// В качестве входного параметра подпрограммы
// *используются указатель (адрес)
 AMumber=&Mumber;
 
// В качестве выходного параметра подпрограммы
// *используются указатель указателя
// (адрес адреса)
 AANumber=&ANumber;
 
 GetValue(AMumber, AANumber); * *// Вызов подпрограммы
 
// Извлечение значения выходного параметра по указателю
 Number=*ANumber;
 Edit1->Text=IntToStr(Number);
 
 Application->ProcessMessages();
}
 
//---------------------------------------------------------------------------

Заголовок:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TButton *Button1;
    TEdit *Edit1;
    TEdit *Edit2;
    void __fastcall Button1Click(TObject *Sender);
    void __fastcall FormActivate(TObject *Sender);
private:    // User declarations
public: *// User declarations
    __fastcall TForm1(TComponent* Owner);
};
//====================================================================//
 typedef bool (WINAPI *TGetValue)(int*, int**); *// ** !
 TGetValue GetValue;
 HINSTANCE hM3_dll;
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Проект простой. На форме одна кнопка и два поля TEdit. По нажатии на кнопку происходит увеличение содержимого Edit2 на единицу и результат заносится в Edit1.

Почему в C++ входной параметр воспринимается как ссылка, а выходной - как ссылка ссылки?
При этом, если основная прога написана на Делфи, то всё ожидаемо: и входной, и выходной параметры воспринимаются как ссылки на числа.

Извращался по всякому. И cdecl ставил, и var вместо out. Один фиг...

Это так задумано, или я где-то накосячил? Полдня лопатил всемирную помойку, ничего не нашёл.
Подскажите, плз., если кто сталкивался.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
08.11.2013, 19:46
Ответы с готовыми решениями:

Передача агрументов в функцию
Каким способом можно сделать такой аналог обратить внимание на это public function...

Топатун с 9 на 16 место - туда:сюда
Вопрос знатокам: Поможите, други, топчусь как слон на одном месте - то 9 место, то 16! Сапа -...

Перебор элементов туда сюда
Добрый день. Есть много объектов. Во что их обернуть, куда их пристроить, что бы я мог установить...

Движение мувиклипа туда-сюда
Не могу сделать что б объект на сцене ездил допустим вправо влево - Тоесть влево, когда координата...

35
0 / 0 / 0
Регистрация: 08.11.2013
Сообщений: 20
12.11.2013, 21:52  [ТС] 21
Author24 — интернет-сервис помощи студентам
Благодарю за анализ кода.

Цитата Сообщение от Avazart Посмотреть сообщение
Память должна освобождаться там же где выделяется ( либо в DLL либо в самой программе, но в одном месте).
- 2. Про использование out и var уже говорил.
1. Память в данном случае может выделяться только в библиотечном модуле, т.к. длина массива ищется в нём же. Поэтому освободиться она не может, т.к. значения массива должны быть переданы в основную программу. Если только попробовать фокус через SetLength(bf,0) (или в варианте С++ запись нуля по смещению ^bf[0,0,0]-4). По идее, указатель ссылается на память, выделенную в DLL, так что можно попытаться.
Правда, чисто из академического интереса, практической пользы от этого никакой нет - массив и килобайта не занимает, а создаётся при работе программы один раз, ну максимум раза 4, а то и не разу. Я заметил, что единственный раз подключённый прибор занимает всегда один и тот же виртуальный COM порт, так что каждый раз при работе программы вызывать эту подпрограмму нет смысла.

2. Если без использования out и var, как можно оперировать указателем- входным параметром, если память под массив ещё не выделена? В Вашем примере Вы через pointer изменяете значение целого, память под которое уже выделена в основной программе. Появилась одна мысль, если до утра не ускользнёт, и завтра на работе не будут доставать, попробую воплотить. Если получится, то и с выделением памяти должно пройти.
0
Эксперт С++
8385 / 6147 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
13.11.2013, 00:03 22
Выделять надо в программе, и там же удалять...
В dll только делать вычисления, по полученным указателям из вне.

Добавлено через 3 минуты
Цитата Сообщение от MrGalaxy Посмотреть сообщение
Поэтому освободиться она не может, т.к. значения массива должны быть переданы в основную программу.
А вы уверенны что не возвращается мусор ? Я нет...

Добавлено через 2 часа 4 минуты
Кроме всего сказанного стоит отметить что дин.массив в Delphi не эквивалентен дин.массиву в С++.
В С++ массивы не содержат счетчика ссылок и "не удаляются сами" при выходе и области видимости, память с под них нужно освобождать руками с помощью delete[]
0
0 / 0 / 0
Регистрация: 08.11.2013
Сообщений: 20
13.11.2013, 11:12  [ТС] 23
Цитата Сообщение от Avazart Посмотреть сообщение
Выделять надо в программе,
Хорошо бы. Я писал, почему в моём случае это невозможно. Поэтому и без директивы out ничего не работает.

Цитата Сообщение от Avazart Посмотреть сообщение
А вы уверенны что не возвращается мусор ? Я нет...
Для этого я и создал тестовый пример, можете убедиться.

Цитата Сообщение от Avazart Посмотреть сообщение
Кроме всего сказанного стоит отметить что дин.массив в Delphi не эквивалентен дин.массиву в С++.
В С++ массивы не содержат счетчика ссылок и "не удаляются сами" при выходе и области видимости, память с под них нужно освобождать руками с помощью delete[]
Ну, я ведь в С++ и не создаю динамический массив.


Проверил освобождение памяти. Добавил в конце обработчика кнопки в Делфи
Delphi
1
  SetLength(bf, 0);
Работает.
При этом, 1-й вариант DLL работает более корректно, нежели 2-й. Если размер массива достаточно большой, то 2-й вариант DLL позволяет обращаться к себе только один раз, повторное обращение даёт сбой. Причём, память освобождается после первого вызова. Ну, да это всё можно посмотреть, присвоив Num:=5000000; (только надо не забыть закомментарить заполнение строк RichEdit !)

В С++ обнуление кол-ва элементов и счётчика массива к освобождению памяти не приводит.
C++
1
2
3
4
5
6
7
8
9
 int *PL;
 PL=(int*)PB;
 
 int  Num;                  // Кол-во элементов массива
 Num=(int)(*(PL-1));
 LabeledEdit1->Text=IntToStr(Num);
 
 (int)*(PL-1)=0;
 (int)*(PL-2)=0;
Т.е. заголовок массива меняется, а память не освобождается. Пока у меня мыслей нет.
0
Эксперт С++
8385 / 6147 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
13.11.2013, 13:27 24
Цитата Сообщение от MrGalaxy Посмотреть сообщение
Ну, я ведь в С++ и не создаю динамический массив.
Именно, если в память в DLL выделяется с помощью Delphi, то программа С++ не знает как ее освободить.
Так что тут нет гарантий, зато есть предположение что есть утечка.
Цитата Сообщение от MrGalaxy;
5328355
Хорошо бы. Я писал, почему в моём случае это невозможно. Поэтому и без директивы out ничего не работает.
Вы не читаете мои посты ? не используйте out передавайте указатель

Добавлено через 58 минут
Цитата Сообщение от MrGalaxy Посмотреть сообщение
В С++ обнуление кол-ва элементов и счётчика массива к освобождению памяти не приводит.
Логично по тому как в С++ нет счетчика ссылок как я писал выше, обнуление приводит к обнулению указателя а не освобождению памяти, ( т.е. к утечке)
0
0 / 0 / 0
Регистрация: 08.11.2013
Сообщений: 20
13.11.2013, 15:31  [ТС] 25
Цитата Сообщение от Avazart Посмотреть сообщение
Вы не читаете мои посты ? не используйте out передавайте указатель
Ох уж эти указатели! Может, и правда в них разбираться начну.
Читал я Ваши посты, только не сразу догадался как передать массив по указателю. Подумал: раз Вы настаиваете, значит и правда возможно.
Вот, выношу на суд общественности:
DLL
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
library M3_78M;
 
type Tbf = array of array[0..1,0..31]of char;
type TTbf = ^Tbf;
 
{$R *.res}
 
function GetInfo(bf: TTbf): boolean; stdcall;
 var k1, k2, k3: integer;
     Num: integer;
Begin
 GetInfo:=false;
 
 Num:=5;
 
 SetLength(bf^, Num);
 for k1:=0 to Num-1 do
   for k2:=0 to 1 do
     for k3:=0 to 31 do
       bf^[k1,k2,k3]:=#00;
 
 bf^[0,0]:='00000000';
 bf^[0,1]:='00001111';
 bf^[1,0]:='11110000';
 bf^[1,1]:='11111111';
 bf^[2,0]:='22220000';
 bf^[2,1]:='22221111';
 bf^[3,0]:='33330000';
 bf^[3,1]:='33331111';
 bf^[4,0]:='44440000';
 bf^[4,1]:='44441111';
 
 GetInfo:=true;
end;
 
{----------------------------------------------------------------------}
Exports GetInfo;
 
begin
end.

Вызов из Делфи
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls, DateUtils;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    LabeledEdit1: TLabeledEdit;
    RichEdit1: TRichEdit;
 
    procedure Button1Click(Sender: TObject);
    procedure FormActivate(Sender: TObject);
 
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
function GetInfo(B: pointer): boolean; stdcall; external 'M3_78M.dll';
 
{$R *.dfm}
 
procedure TForm1.Button1Click(Sender: TObject);
 var PB0: pointer;
     k: integer;
     Num: integer;
     bf: array of array [0..1,0..31] of char;
Begin
 RichEdit1.Clear;
 LabeledEdit1.Clear;
 Application.ProcessMessages;
 Sleep(200);
 
 SetLength(bf, 0);
 PB0:=@bf;
 
 GetInfo(PB0);
 
 Num:=Length(bf);
 LabeledEdit1.Text:=IntToStr(Num);
 With RichEdit1.Lines do
  begin
    for k:=0 to Num-1 do
     begin
       Add(' '+bf[k, 0]+'         '+bf[k, 1]);
       Application.ProcessMessages;
     end;
    Add('');
    Add('');
  end;
 
  SetLength(bf, 0);
end;
 
{----------------------------------------------------------------------}
procedure TForm1.FormActivate(Sender: TObject);
begin
 with Button1 do
   if Enabled then
     SetFocus;
end;
 
end.
0
Эксперт С++
8385 / 6147 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
13.11.2013, 15:38 26
Цитата Сообщение от MrGalaxy Посмотреть сообщение
SetLength(bf^, Num);
for k1:=0 to Num-1 do[/DELPHI]
Не вижу отличий ...
0
0 / 0 / 0
Регистрация: 08.11.2013
Сообщений: 20
13.11.2013, 16:07  [ТС] 27
Цитата Сообщение от Avazart Посмотреть сообщение
Не вижу отличий ...
отличий от чего?

Отличие в отсутствии out, теперь указатель на массив является входным параметром.
Да, и что-то из С++ теперь перестало вызываться.

Что в out некорректного? С ним работает.
0
Эксперт С++
8385 / 6147 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
13.11.2013, 16:09 28
Цитата Сообщение от MrGalaxy Посмотреть сообщение
отличий от чего?
От предыдущих примеров, как и прежде память выделяется внутри DLL
Цитата Сообщение от MrGalaxy Посмотреть сообщение
Что в out некорректного? С ним работает.
Его нет в С++
0
0 / 0 / 0
Регистрация: 08.11.2013
Сообщений: 20
13.11.2013, 19:14  [ТС] 29
Цитата Сообщение от Avazart Посмотреть сообщение
От предыдущих примеров, как и прежде память выделяется внутри DLL
А! Это - условие, по другому не будет. В том и вся сложность.
Цитата Сообщение от MrGalaxy Посмотреть сообщение
Память в данном случае может выделяться только в библиотечном модуле, т.к. длина массива ищется в нём же.


Цитата Сообщение от Avazart Посмотреть сообщение
Его нет в С++
Ну это ничего страшного. Критерий - работоспособность программы, а она работает. Корректней, чем без out. Вы не пробовали оттранслировать и запустить мои проекты? Можете убедиться.
0
Эксперт С++
8385 / 6147 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
13.11.2013, 19:31 30
Цитата Сообщение от MrGalaxy Посмотреть сообщение
Память в данном случае может выделяться только в библиотечном модуле, т.к. длина массива ищется в нём же.
Выделяйте память извне с запасом из расчета максимальной длины, или в два захода сначала вызов ф-ции для поиска длины, потом самих вычислений.

Добавлено через 4 минуты
Цитата Сообщение от MrGalaxy Посмотреть сообщение
Критерий - работоспособность программы, а она работает.
На утечку памяти проверяли ?
Если есть утечка, то о какой работоспособности идет речь ?
Если нет, то все ок.
0
0 / 0 / 0
Регистрация: 08.11.2013
Сообщений: 20
14.11.2013, 09:02  [ТС] 31
Цитата Сообщение от Avazart Посмотреть сообщение
Выделяйте память извне с запасом из расчета максимальной длины, или в два захода сначала вызов ф-ции для поиска длины, потом самих вычислений.
Один из путей. Некрасивый. И всё из-за того, что кое-кто у нас в конторе не пишет на Делфи. Второй путь, который мне тоже не очень нравится - массив заполняется, выводится на экран, оператор считывает информацию, вручную вводит куда надо, и далее идёт уже процесс автоматизированных измерений (так я поначалу написал DLL-ку и так объяснил тем, кто с ней будет работать). Потом решил поиграться, освоить технологию использования дин. массивов. Но, видимо, придётся отказаться от этой затеи, не надо умничать на ровном месте.

Цитата Сообщение от Avazart Посмотреть сообщение
На утечку памяти проверяли ?
Делфийская DLL и основная программа C++, передача агрументов туда-сюда


Не по теме:

Да простят меня поклонники С++, ничего не имею против этого языка, но когда я начал писать на паскале, мне он понравился сразу. Сейчас, столкнувшись впервые с С++, получил о нём пока неприятные впечатления.




Avazart,
Вам огромное спасибо за потраченное на меня время, за отсутствие снобизма в общении с дилетантом, я действительно кое-чему научился за эти дни.
0
Эксперт С++
8385 / 6147 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
14.11.2013, 15:41 32
Языки разные, но тут больше принципы с которыми нужно руководствоваться при написании DLL.

C++ тут вообще никаким боком, попробуйте загрузить с C# думаю будут те же проблемы.

Добавлено через 6 минут
Цитата Сообщение от MrGalaxy Посмотреть сообщение
Один из путей. Некрасивый. И всё из-за того, что кое-кто у нас в конторе не пишет на Делфи.
А смысл писать на Delphi если нет специалистов которые его потом будут поддерживать.
Кроме того вроде как можно еще попробовать COM- технологию, но лично мне такой вариант не нравится, на мой взгляд зная полную задачу вполне достаточно обойтись DLL.

Добавлено через 12 минут
Будет время возможно напишу пример ...
0
0 / 0 / 0
Регистрация: 08.11.2013
Сообщений: 20
14.11.2013, 17:11  [ТС] 33
Цитата Сообщение от Avazart Посмотреть сообщение
А смысл писать на Delphi если нет специалистов которые его потом будут поддерживать.
Кроме того вроде как можно еще попробовать COM- технологию, но лично мне такой вариант не нравится, на мой взгляд зная полную задачу вполне достаточно обойтись DLL.
Я предложил свои услуги в написании всей программы полностью, мне ответили, что есть программисты, они напишут, а меня привлекли как специалиста, имеющего опыт стыковки приборов с ЭВМ по КОПу. Поэтому так и получилось: я не могу писать на С, т.к. совсем его не знаю (а результат нужен как всегда вчера ), а программисты не хотят писать на Делфи по неизвестным мне причинам.

Цитата Сообщение от Avazart Посмотреть сообщение
Будет время возможно напишу пример ...
Буду ждать с нетерпением.
0
Эксперт С++
8385 / 6147 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
14.11.2013, 17:37 34
Цитата Сообщение от MrGalaxy Посмотреть сообщение
Я предложил свои услуги в написании всей программы полностью, мне ответили, что есть программисты, они напишут, а меня привлекли как специалиста, имеющего опыт стыковки приборов с ЭВМ по КОПу.
Пс) Так это не выгодно для Вас, как по мне, особенно если речь о оплачиваемой работе.

Есть такая идея:

Создавать массив глобально внутри DLL и будет предполагаться что, что бы очистить память необходимо вызвать специальную ф-цию очистки, которая и будет обнулять массив внутри DLL, как бы это нормально для Си, выделение памяти внутри одной ф-ции а освобождение в другой. Но обычно это делается относительно дескриптором передаваемых в ф-цию.

Добавлено через 5 минут
Вот пример использования Сишной библиотеки

C++
1
2
3
CURL *curl_handle = curl_easy_init(); // тут все подготовительные действия.
// еще что-то
curl_easy_cleanup(curl_handle);  // очистка
В любом случае об особом удобстве не приходится говорить так как что бы DLL была переносимой приходится опускаться на уровень языка Си ( именно Си не С++) , с обоих сторон что со стороны библиотеки на Delphi что со стороны приложения на С++.
Обычно в таких случаях, для удобства приходится еще дополнительно писать обвертку на языке программы, что бы обвернуть, к примеру, код ф-ций в класс контролирующий выделение/удаление памяти сам.
0
0 / 0 / 0
Регистрация: 08.11.2013
Сообщений: 20
15.11.2013, 16:55  [ТС] 35

Не по теме:

Цитата Сообщение от Avazart Посмотреть сообщение
Пс) Так это не выгодно для Вас, как по мне, особенно если речь о оплачиваемой работе.
У нас в конторе вообще оплачивать работу не принято.%-) :)



Цитата Сообщение от Avazart Посмотреть сообщение
Создавать массив глобально внутри DLL и будет предполагаться что, что бы очистить память необходимо вызвать специальную ф-цию очистки, которая и будет обнулять массив внутри DLL,
Так, наверное, и сделаю, по крайней мере, от добавления этой функции в бибилиотеку хуже не будет. Хоть мусора и мало, но, по-хорошему, надо бы его подчищать.

Цитата Сообщение от Avazart Посмотреть сообщение
Вот пример использования Сишной библиотеки
C++
1
2
3
CURL *curl_handle = curl_easy_init(); // тут все подготовительные действия.
// еще что-то
curl_easy_cleanup(curl_handle);  // очистка
Благодарю, попробую изучить.

Добавлено через 6 часов 49 минут
Ура! Посмотрите, что оказывается есть:
http://docwiki.embarcadero.com... namicArray
На Хоботе подсказали.
0
Эксперт С++
8385 / 6147 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
15.11.2013, 18:04 36
Цитата Сообщение от MrGalaxy Посмотреть сообщение
Ура! Посмотрите, что оказывается есть:
http://docwiki.embarcadero.com... namicArray
На Хоботе подсказали.
Отбой, такая штука, как я думаю, есть только в Еmbarcadero т.е С++Builder, кроме того мне слабо верится что его можно использовать в DLL в качестве аналога.
0
15.11.2013, 18:04
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
15.11.2013, 18:04
Помогаю со студенческими работами здесь

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

Как сделать прогресс бар туда-сюда в С++ Builder 6?
В .NET можно менять bricks или marquee,а в билдере как?

1С или Веб?Не могу выбрать! Кидает туда-сюда.Сомнения
Ребят, не могу выбрать что делать...Помогите пожалуйста! Мне 27, вышки нет, заочно 3 курса эконом,...

Разработайте приложение, в котором от одного до другого края формы должна туда-сюда проезжать машина
Разработайте приложение, в котором от одного до другого края формы должна туда-сюда проезжать...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
36
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru