-
Значит так. Пишу ДЛЛку на Дельфи, в которой присутствуют интерфейсы. Эта библиотека юзается сторонними программами с участием интерфейсов. Есть часть исходников этой же ДЛЛки на С++. Вот куски, касающиеся интерфейсов: class CSteamInterface006 : public CSteamInterface005
;
unsigned int _f
Вопрос - как это переделать на Дельфи? Просто я в интерфейсах - нуб, а тут еще вопрос - получится ли солвместить Си с Дельфи?
-
Что-то я не увидел тут никаких интерфейсов. Обычный сишный класс, разве что слово Interface в названии присутствует
-
Я в Си нуб, о чем и сказал, но мне на инглиш форуме сказали, что там именно интерфейс и в этом коде: unsigned int _f
идет передача Сишной программе интерфейса. Если делать в Дельфи CSteamInterface006 классом, то нифига не работает, поскольку компилер показывает, что процедуры этого класса не собраны в ДДЛку. + если все-таки оставить его, то при вызове этого интерфейса вылазит ошибка чтения памяти :( в том районе, где ничего нету Если же сделать именно интерфейс, то ошибка памяти есть, но в районе, где находится неизвестный код (судя по всему - основной программы)
-
> но мне на инглиш форуме сказали, что там именно интерфейс > и в этом коде: > unsigned int _f > { > static CSteamInterface006 SteamInterface006; > return (unsigned int)&SteamInterface006; > } > идет передача Сишной программе интерфейса.
Тут передается просто ссылка на созданный (причем static) объект. Причем здесь интерфейсы? Переписать - не проблема (наверно:). Один в один переписывай весь код на дельфи. В ЕХЕ тебе нужно создать аналогичный класс, только абстрактный. Например: TSteamInterface006 = class(TSteamInterface005)
public
function FindServersNumServers(arg1: Cardinal): Integer; virtual; stdcall; abstract;
function FindServersIterateServer(arg1, arg2: Integer; szServerAddress: PChar; uServerAddressChars: Integer): Integer; virtual; stdcall; abstract;
function FindServersGetErrorString(): Integer; virtual; stdcall; abstract;
end; Далее объявляешь переменную var
ASteamInterface: TSteamInterface006; Получаешь ссылку на объект: Cardinal(ASteamInterface) := _f(); И работаешь с объектом через абстрактные методы. В рельности же будут вызваны функции из DLL. Не уверен на 100%, что удасться совместить код С++ и Дельфи, однако предполагаю, что получится. Виртуальные и абстрактные методы во всех языках, поддерживающих интерфейсы, реализованы одинаково (таблица виртуальных методов имеет везде одинаковое смещение и т.д.) (это имхо, т.к. все - по памяти). Однако я не понял, нужно ли тебе совмещать С++ и Дельфи, или же просто переписать все на Дельфи?
-
> public CSteamInterface005
описание в студию
-
> > public CSteamInterface005 > > > описание в студию >
Зуб даю, что примерно так начинается: class CSteamInterface005 : public CSteamInterface004 :-)) > Что-то я не увидел тут никаких интерфейсов.
насколько я понимаю бинарная структура интерфейсов и дельфийских и сишных классов - совпадает. главное чтоб первые три элемента VMT базового предка классов имели ссылки на AddRef/Release/QueryInterface
-
На самом деле (сужу по старым версиям, новых версий не знаю) в языке С++ нет интерфейсов. Слово interface является синонимом слова class, а интерфейсом называется класс, который не содержит полей, а только виртуальные абстрактные методы. Чтобы класс вел себя как интерфейс требуется писать дополнительный код.
-
> На самом деле (сужу по старым версиям, новых версий не знаю) > в языке С++ нет интерфейсов.
естественно нет. они не нужны так как
> бинарная структура интерфейсов и дельфийских и сишных классов
А вот в дельфи интерфейсы нуны, так как классы не поддерживают множественное наследование..
-
Есть такая штука, называется COM
Сишная библиотека делается COM-сервером, в котором реализован интерфейс. Программа на дельфе - клиент, обращающийся к серверу и получающий указатель на интерфейс. И всё. Не надо использовать никаких кривых абстрактных классов и прочей мути. Если нет желания прописывать длл в реестр, то можно обойтись и без этого. Тогда сервер должен экспортировать одну нестандартную функцию создания объекта.
-
Я ж так понимаю, у него хостовая часть приложения уже "дана свыше" и на Ц++ (собственно, и догадаться несложно, о каком приложении идет речь -Steam). А он, читая прилагаемые к ней Цплюсплюсные хедеры, пытается сочинить клиента на Delphi.
-
Я вообщето не сочиняю клиента Стима, а делаю обертку к эмулятору, что требуют несколько проектов. Протсо без этого надо изучать кучу форматов файлов, а так - все нихтяк :) И все-бы хорошо, но игры, в которых этот эмуль рабоает, взаимодействуют только через эти интерфейсы. Если бы были просто вызовы, я бы сюда не обращался, тк заголовки всех 156функций уже переведены :) Но эти интерфейсы.... :( Никак не могу заставить работать и все тут :(
-
> Сишная библиотека делается COM-сервером, в котором реализован > интерфейс. Программа на дельфе - клиент
Я вообщето пишу Дельфийскую библиотеку для Сишной проги, а не наоборот ;) Я просто говорои, что есть часть исходников этой библиотеки на Си
-
Итак, вроде как со всем разобрался. Но вот вопрос - как передать интерфейс? В Сишной ДЛЛке: return (unsigned int)&SteamInterface006; В моей ДЛЛке: SteamInterface006:=CSteamInterface006.Create;
result:=uint(@SteamInterface006); Правильно, или надо както по-другому?
-
> Правильно, или надо както по-другому?
Собаку убери.
Еще: конструкции получились неравноценны. В С++ у тебя объект создается только 1 раз. А в Дельфи - при каждом вызове экспортируемой функции.
-
> Я вообщето пишу Дельфийскую библиотеку для Сишной проги, > а не наоборот ;)
Тогда тем более надо библиотеку делать COM-сервером. Кстати на Delphi это сделать немного проще.
-
> Собаку убери.
ОК > Тогда тем более надо библиотеку делать COM-сервером. Кстати > на Delphi это сделать немного проще.
Тыкни в место, где рассказывается об этом, а то я СОМ не изучал :( > Еще: конструкции получились неравноценны. В С++ у тебя объект > создается только 1 раз. А в Дельфи - при каждом вызове экспортируемой > функции.
Может так?: if SteamInterface006=nil then
SteamInterface006:=CSteamInterface006.Create;
result:=uint(SteamInterface006);
-
Хм, все-равно ЕРРОР :( Посмотрите, может я объявил не правильно?: CSteamInterface006 = class (CSteamInterface005)
...
CSteamInterface005 = class (CSteamInterface004)
...
CSteamInterface004 = class (CSteamInterface003)
...
CSteamInterface003 = class (TInterfacedObject) ?
-
> CSteamInterface003 = class (TInterfacedObject)
Скорее всего CSteamInterface003 = class (TObject) Чего ты за слово "интерфейс" уцепился? Нет там никаких интерфейсов, не лепи их. Это просто ссылки на классы.
-
> Скорее всего CSteamInterface003 = class (TObject)
Сделал. Эффекта 0. Скинул полные исходники того, что наработал, может кто поможет :( http://ifolder.ru/6925349
-
Забыл сказать. Проблемный код находится в Steam_Interface.pas (функция "function _f(cszSteamInterfaceVersion: pChar): uint; export; cdecl;").
-
> Эффекта 0
Тебе ж русским языком говорят - нет в коде в [0] никаких интерфейсов ! Там фигурирует декларация некоего С-шного класса и ф-ция, возвращающая результатом объект этого класса. Никакими "интерфейсами" там не пахнет.
-
-
-
> сделал без интерфейсов - не работает
А кто тебе вообще сказал, что "это" должно работать ?
Ты пойми - COM/OLE-технология на то и существует, чтобы могли взаимодействовать между собой программные модули, созданные в разных средах разработки. И эти модули действительно взаимодействуют при посредстве механизма интерфейсов. Но в приведенном тобой коде нет ничего, что могло бы хоть как-то подтвердить, что интересующая библиотека действительно использует этот механизм.
-
Вот, как объявляется в Сишном коде: class CSteamInterface006 : public CSteamInterface005
.....
class CSteamInterface005 : public CSteamInterface004
.....
class CSteamInterface004 : public CSteamInterface003
.....
class CSteamInterface003
Я объявляю: CSteamInterface006 = class (CSteamInterface005)
..
end;
.....
CSteamInterface005 = class (CSteamInterface004)
..
end;
.....
CSteamInterface004 = class (CSteamInterface003)
..
end;
.....
CSteamInterface003 = class (TInterfacedObject)
..
end; В Сишном коде я там ничего про СОМ не нашел!
-
Ты хоть [3] читал? Пробовал?
-
> Ты хоть [3] читал? Пробовал?
Пробывал. Мне нужен реальный обработчик событий, те: function CSteamInterface006.FindServersNumServers(arg1: uint): int;
begin
result:=SteamFindServersNumServers(arg1);
end; Еслиже ставить abstract, то ругается, что нельзя это писать. Как же тогда сделать обработчик?
-
Хм, сделал без SteamInterface006:=CSteamInterface006.Create; , а просто result:=uint(SteamInterface006); все ОК. Только вот сама программа теперь ругается: CFileSystem_Steam::Init()Failed: failed to find steam interface + компиллер показывает, что обработчики событий интефейсов (не в прямом смысле слова) не вомпилируются. Судя по все му, из-за этого и ошибка.
-
Ну во первых, это таки интерфейсы, причем бинарно идентичные COM, собственно COM-интерфейсы в плюсах так и объявляются. Но с двумя отличиями: 1) Нету стандартных COM методов QueryInterface, AddRef, Release (в С++ их нужно явно объявлять) 2) Соглашение о вызове - cdecl. Первый пункт не дает воспользоваться дельфовыми COM-интерфейсами - избавиться от трех стандартных методов невозможно (AFAIK). Поэтому делается несколько извращенно. Применительно к этим сорцам: 1) Необходимо в CSteamInterface* (в Delphi кстати все-таки принято вместо C использовать префикс T) и CSteamDLLAppsystem все методы объявить как virtual; cdecl; 2) Немного изменить Steam_Interface.pas: unit Steam_Interface;
interface
uses
Windows, KOL,
utils, SteamTypes, Steam_Interface_1, Steam_Interface_2, Steam_Interface_3,
Steam_Interface_4, Steam_DLLAppsystem, Steam_Misc;
function CreateInterface(cszSteamDLLAppsystemInterfaceVersion: pChar;
pError: PSteamError): Pointer; export; cdecl;
function _f(cszSteamInterfaceVersion: pChar): Pointer; cdecl;
var
SteamDLLAppsystem: CSteamDLLAppsystem001;
SteamInterface003: CSteamInterface003;
SteamInterface004: CSteamInterface004;
SteamInterface005: CSteamInterface005;
SteamInterface006: CSteamInterface006;
implementation
function CreateInterface(cszSteamDLLAppsystemInterfaceVersion: pChar;
pError: PSteamError): Pointer; export; cdecl;
begin
Log('CreateInterface SteamDLLAppsystem version:' +cszSteamDLLAppsystemInterfaceVersion+#13#10);
result:=nil;
if cszSteamDLLAppsystemInterfaceVersion='SteamDLLAppsystem001' then
begin
SteamClearError(pError);
result:=SteamDLLAppsystem;
end;
end;
function _f(cszSteamInterfaceVersion: pChar): Pointer; export; cdecl;
begin
Log('_f SteamInterface version: '+cszSteamInterfaceVersion+#13#10);
result:=nil;
if cszSteamInterfaceVersion='Steam003' then Result:=SteamInterface003
else if cszSteamInterfaceVersion='Steam004' then Result:=SteamInterface004
else if cszSteamInterfaceVersion='Steam005' then Result:=SteamInterface005
else if cszSteamInterfaceVersion='Steam006' then Result:=SteamInterface006
end;
initialization
SteamDLLAppsystem:=CSteamDLLAppsystem001.Create;
SteamInterface003:=CSteamInterface003.Create;
SteamInterface004:=CSteamInterface004.Create;
SteamInterface005:=CSteamInterface005.Create;
SteamInterface006:=CSteamInterface006.Create;
finalization
SteamDLLAppsystem.Free;
SteamInterface003.Free;
SteamInterface004.Free;
SteamInterface005.Free;
SteamInterface006.Free;
end. 3) Там, где ругнется компилер (один из методов ...Appsystem...) - привести к int. После этого компилится и даже позволяет запустить распакованный Sin.
-
Ах да, совсем забыл. CSteamInterface003 и CSteamDLLAppsystem001 нужно наследовать от TObject, а не TInterfacedObject. Последний как раз реализует те самые методы COM, отсутствующие в интерфейсах стима.
-
> Применительно к этим сорцам:
Спасибо, интерфейс заработал :) Насчет СИНа - он не юзает интерфейсы, а просто смотрит, приобрете на ли игра, а это у меня уже давно реализованно ;) Да и тем болле те сырцы - это самостоятельная ДЛЛка, а я сейчас пишу врапер к SteamEmu для перехвата обращения к файлам (как минимум).
-
Ну он не только смотрит на купленность, но и использует SteamFS. Но в целом да, я его потому и выбрал, что он имеет минимум требований к длл-ке.
|