OS: Windows XP SP3.
Delphi: 2010
C++:
CodeBlock 10.5
VisualStudio: 2008
.NeT Framework установленный в системе: 3.5 sp1.
Я начинающий программист. Прошу прощения если такие посты уже были - я не нашел на форуме. Лично я более разбираюсь в Pascal, чем в C++. Но, к сожалению очень большое количество текстов написано на C++. (По большей части меня интересует Windows Development Kit)
 
 В интернете, как я почитал, существует множество способов импорта класса в Delphi из dll, написанной в VisualC++. Из тех способов на мой взгляд самым быстрым является использование параметра __declspec(dllexport) перед именем класса и создание функции, с помощью которой создаётся экземпляр класса и в дальнейшем обращение к этому экземпляру. После компиляции на Visual C++, получаем dll которую в дальнейшем пытаюсь использовать.
Проблема: Использование dll в Delphi 2010 происходит нормально, однако при использовании данной библиотеки в Lazarus'е, компиляция и компоновка проходят нормально, но при запуске полученной программы происходит вот что:
ok
was import
exception at 006E0049:
Access violation.
Вот код на C++:
- Код: Выделить всё
- //other_npob.h
 class __declspec(dllexport) CIncomeImp
 {
 public:
 virtual double __stdcall GetIncome( double aNetto );
 virtual void __stdcall FreeObject();
 int a;
 };
 extern "C" __declspec(dllexport) CIncomeImp *CreateIncome();
 // other_npob.cpp: определяет процедуры инициализации для DLL.
 #include "stdafx.h"
 #include "other_npob.h"
 double __stdcall CIncomeImp::GetIncome( double aNetto )
 {
 printf("yryn");
 a=55;
 return aNetto;
 }
 void __stdcall CIncomeImp::FreeObject()
 {
 delete this ;
 }
 CIncomeImp *CreateIncome()
 {
 return new CIncomeImp ;
 }
Код на Delphi:
- Код: Выделить всё
- program Project1;
 {$APPTYPE CONSOLE}
 uses
 SysUtils,
 unit1 in 'unit1.pas';
 var
 incomeRef: IIncome; //member of the reference
 cIncome: Double;
 function CreateIncome: IIncome; cdecl; external('tmp.dll');
 begin
 { TODO -oUser -cConsole Main : Insert code here }
 incomeRef:=createIncome;
 writeln('was import');
 writeln(incomeRef.a);
 cIncome:=incomeRef.GetIncome(22);
 writeln(cIncome);
 writeln(incomeRef.a);
 end.
 //unit1.pas
 unit Unit1;
 interface
 type
 IIncome = class
 public
 a:integer;
 function GetIncome(const aNetto: double): double; virtual; stdcall; abstract;
 procedure FreeObject; virtual; stdcall; abstract;
 end;
 
 implementation
 end.
Код на Lazarus'е:
- Код: Выделить всё
- program project1;
 {$mode delphi}
 uses
 ShareMem,
 {$IFDEF UNIX}{$IFDEF UseCThreads}
 cthreads,
 {$ENDIF}{$ENDIF}
 Classes, SysUtils, CustApp, unit1
 { you can add units after this };
 type
 { TMyApplication }
 TMyApplication = class(TCustomApplication)
 protected
 procedure DoRun; override;
 public
 constructor Create(TheOwner: TComponent); override;
 end;
 { TMyApplication }
 function CreateIncome: IIncome; cdecl; external('other_npob.dll');
 procedure TMyApplication.DoRun;
 var
 ErrorMsg: String;
 incomeRef: IIncome; //member of the reference
 cIncome: Double;
 begin
 { add your program here }
 writeln('ok');
 incomeRef:=createIncome;
 writeln('was import');
 cIncome:=incomeRef.GetIncome(22);
 // stop program loop
 Terminate;
 end;
 constructor TMyApplication.Create(TheOwner: TComponent);
 begin
 inherited Create(TheOwner);
 StopOnException:=True;
 end;
 var
 Application: TMyApplication;
 {$IFDEF WINDOWS}{$R project1.rc}{$ENDIF}
 begin
 Application:=TMyApplication.Create(nil);
 Application.Title:='My Application';
 Application.Run;
 Application.Free;
 end.
 //unit1.pas
 unit unit1;
 {$mode delphi}
 interface
 uses
 ShareMem,Classes, SysUtils;
 type
 IIncome = class
 public
 a:integer;
 function GetIncome(const aNetto: double): double; virtual; stdcall; abstract;
 procedure FreeObject; virtual; stdcall; abstract;
 end;
 implementation
 end.
Заранее благодарю за любую помощь.

Добавлено спустя 8 часов 19 минут 46 секунд:
Эх...Никто меня не любит, отвечать мне не хочет, все меня игнорируют...
 ...Ну тогда сам с собой буду разговаривать..
 ...Ну тогда сам с собой буду разговаривать..  ..
 ..  
 Насколько я разобрался во усём виновата Virtual Method Table..Редиска...
Насколько можно судить из http://en.wikipedia.org/wiki/Virtual_method_table указатель на первый виртуальный метод в VMT си плюса начинается с +0, в то время как в Lazarus'е, судя по статье http://www.freepascal.org/docs-html/pro ... 02-2080027 указатель на первый метод начинается толи с +16, толи с +20 (подскажите кстати откуда точно)..А предыдущие будут 20(или 16) байт будут заполнены служебной инфой...
Отсюда следует несколько простых, аки наковальня, решений проблем возникших у меня с Lazarus'ом:
1) Перейти на Delphi. Не подходит - я жадный и столько денег, сколько просят за эту чудесную программу не дам..
 
 2) Использовать "обёртки" и через них вызывать методы класса. В моём прожекте (слово-то какое
 ) можно не торопиться, поэтому пока что, не хочу заниматься этим..Хм..Интересным занятием..
 ) можно не торопиться, поэтому пока что, не хочу заниматься этим..Хм..Интересным занятием..  ..
 ..3) Найти то маленькое различие (
 ) между работой Delphi и Lazarus'а и устранить его. Честно говоря я знаю, что я - негодяй, подлец и вообще не понимаю сколько тут работы и ничего не смогу сделать, потому что мало что смыслю в профессиональном программировании на ООП..
 ) между работой Delphi и Lazarus'а и устранить его. Честно говоря я знаю, что я - негодяй, подлец и вообще не понимаю сколько тут работы и ничего не смогу сделать, потому что мало что смыслю в профессиональном программировании на ООП..  ..Полностью согласен и принимаю данное замечание, надеюсь что к нему мы больше не вернёмся..
 ..Полностью согласен и принимаю данное замечание, надеюсь что к нему мы больше не вернёмся..  ..
 ..Итак, как мне кажется я нашёл одно различие в работе этих великолепных сред программирования - это VMT таблицы. У меня возник такой вопрос а можно ли "подогнать" таблицу VMT C++ и Lazarus'а. К примеру добавить в код C++ несколько "методов-болванок", так чтобы полезные нам методы класса начинались как раз-таки с +16(или 20) байта?
 
 И второй вопрос можно ли как-нибудь посмотреть VMT, которая формируется в исполняемой программе?
Кто-нибудь помогите..
 ..
 .. 
Итак, несмотря ни на что (на нескольких форумах меня прямо послали в...интернет изучать обёртки, кое-где вообще заявили - проблема неразрешима, а кое-где вообще ничего не отвечают
 )..Я всё-таки разрешил проблему в одну строчку...
 )..Я всё-таки разрешил проблему в одну строчку...  
 Ещё раз скажу, что программист я начинающий, поэтому ногами меня не бить и всё описанное далее ИМХО..!..Проблему я написал выше. Решение в общем-то, как оказалось такое же простое как и математика, к сожалению не такое постоянное. И - я угадал, действительно, подогнать таблицу VMT C++ к таблице Lazarusa можно. После создания Dll'кой экземпляра класса, где-то в куче по формату vmt си плюса класс начинается с +0, однако по формату vmt Lazarus'а (кто-нибудь объясните мне, кстати - зачем Lazarus так с ней поступил (с таблицей) ) pointer на класс будет начинаться с +12...Отсюда получаем, что после возврата Dll'кой указателя (на область памяти с которой начинается экзэмпляр класса) он указывает на +0. Lazarus же топает по этому адресу...думает...прибавляет свои +12 байт и думает, что класс тута с +12, что конечно неправда.. Естественно, что решением проблемы является простая математика:
dec(PInteger(incomeRef)^, $12);
И теперь мы должны иметь "правильный" указатель на класс..Но не тут-то было - вот нету тута класса и усё
 ...Поэтому делаем Цикл и просматриваем всю кучу до -88 (-88 потому что http://www.rsdn.ru/article/Delphi/delphiabs.xml последний указанный -76, и вероятно не просто так ну и + наши 12байт = 88 байт проверки) А просматриваем - банально проверкой:
 ...Поэтому делаем Цикл и просматриваем всю кучу до -88 (-88 потому что http://www.rsdn.ru/article/Delphi/delphiabs.xml последний указанный -76, и вероятно не просто так ну и + наши 12байт = 88 байт проверки) А просматриваем - банально проверкой:is Нужный_Нам_Класс
Ну это общее решение возникшей проблемы, т.к. как мне кажется в будущем опять обновят компилятор C++, опять сменят компилятор Lazarus, че-нить вгрызут в сам Windows и опять будет другое смещение. В моём случае верное смещение оказалось на 50 байтов ниже, чем мне подсовывал (как я понимаю) компилятор Lazarus'а. Т.е. :
dec(PInteger(incomeRef)^, $50);
Ну да, так ничего не понять, наверно будет начинающему программисту, так что вот полный код:
Код на VisualC++:
- Код: Выделить всё
- //other_npob.h
 //
 class __declspec(dllexport) CIncomeImp
 {
 public:
 virtual int __stdcall GetIncome( int aNetto );
 virtual void __stdcall FreeObject();
 };
 extern "C" __declspec(dllexport) CIncomeImp *CreateIncome();
 // other_npob.cpp: определяет процедуры инициализации для DLL.
 //
 #include "stdafx.h"
 #include "other_npob.h"
 int __stdcall CIncomeImp::GetIncome( int aNetto )
 {
 printf("yry\n");
 return aNetto;
 }
 void __stdcall CIncomeImp::FreeObject()
 {
 delete this ;
 }
 CIncomeImp *CreateIncome()
 {
 return new CIncomeImp;
 }
 //other_npob.def
 ; other_npob.def: объявляет параметры модуля для DLL.
 LIBRARY "other_npob"
 EXPORTS
 ; Сюда можно направлять явные операции экспорта
 GetIncome
Код на Lazarus'е:
- Код: Выделить всё
- //unit1.pas
 unit unit1;
 interface
 type
 IIncome = class
 public
 function GetIncome(aNetto: integer): integer; virtual; stdcall; abstract;
 procedure FreeObject; virtual; stdcall; abstract;
 end;
 function CreateIncome():IIncome; stdcall; external('other_npob.dll');
 implementation
 end.
 //project1.lpr
 program project1;
 uses
 unit1;
 var
 incomeRef: IIncome;
 cIncome: integer;
 begin
 writeln('ok');
 incomeRef:=createIncome(); //создаём экзэмпляр класса
 writeln('was import'); //пишем на экран, что усё ОК
 dec(PInteger(incomeRef)^, $50); //из-за разных форматов таблиц VMT (ИМХО) приходится извращаться в куче
 cIncome:=incomeRef.GetIncome(6); //собственно проверяем работу класса
 writeln(cIncome); //проверяем что занесено в проверочную переменную
 end.
У меня работает...
 ..
 ..Итак повторюсь решение в $50 байтиков конкретное, для данных компиляторов(Лазаруса и C++). Для решения общего смотри пост выше..
 ..Хотя я уверен - что это плохой и ужасный стиль программирования..Но всё-таки..
 ..Хотя я уверен - что это плохой и ужасный стиль программирования..Но всё-таки..Вот и усё..
 ..Большая просьба модераторам: то что я написал я не нашёл в интернете на русском языке "всё сразу и вместе", однако видел очень много вопросов подобного рода в интернете как на русском языке, так и на английском:
 ..Большая просьба модераторам: то что я написал я не нашёл в интернете на русском языке "всё сразу и вместе", однако видел очень много вопросов подобного рода в интернете как на русском языке, так и на английском:http://community.freepascal.org:10000/b ... m_id=24090
http://www.mail-archive.com/fpc-devel@l ... 01598.html
http://community.freepascal.org:10000/b ... m_id=24097
viewtopic.php?f=14&t=3628
Да и молчание по этому вопросу здесь, где я задал этот животрепещущий вопрос о многом говорит..
 
 Кстати, кто мне со знанием дела объяснит почему именно +$50 байтов, тому...морс (что? почему не пиво? А пиво - вредно..
 )
  ) Большая просьба модераторам: как мне кажется вы очень обяжете Многих и Многих новичков как я, если внесёте хотя бы часть из того бреда, что я написал выше (я понимаю, что скорей всего написал половину - неправильно
 Большая просьба модераторам: как мне кажется вы очень обяжете Многих и Многих новичков как я, если внесёте хотя бы часть из того бреда, что я написал выше (я понимаю, что скорей всего написал половину - неправильно   ) в FAQ.
 ) в FAQ.   Это, действительно печально, когда любимая бесплатная программа отказывается делать то же, что её платный аналог..
  Это, действительно печально, когда любимая бесплатная программа отказывается делать то же, что её платный аналог..Но главное - мы победили и всё работает..
 
   Всем удачи!..
 Всем удачи!..



