Экспорт глобального символа из библиотеки DLL Delphi
Я пытаюсь создать Gecko 2.0-совместимую DLL в Delphi.
ранее (pre-Gecko 2.0) DLL, необходимая для экспорта функции NSGetModule (). Это сработало безупречно.
начиная с Firefox 4, Моя DLL загружается (я проверил это, хотя точка останова в моем разделе инициализации), но моя функция NSGetModule () больше не вызывается. Это поведение, потому что начиная с Gecko 2.0 (в Firefox 4), двоичный компонент не должен для экспорта функции NSGetModule ():
https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0#Binary_components
согласно этим документам, моя DLL должна экспортировать символ данных NSModule, который указывает на структуру. В терминологии Delphi я предполагаю, что это глобальная переменная, которая указывает на запись Delphi.
В C++ это то, как вы экспортируете (глобальный) символ данных:
define NSMODULE_DEFN(_name) extern "C" NS_EXPORT mozilla::Module const *const NSModule
мой вопрос: как мне это сделать в Дельфах? Как экспортировать глобальную переменную?
Я ценю Ваше мнение.
5 ответов
Delphi экспортирует глобальные переменные из DLL аналогично тому, как он экспортирует функции:
library exp;
var
global: Integer;
exports global;
end.
Delphi может импортировать глобальные переменные из DLL, но это немного Хак: объявить процедуру импорта DLL с тем же именем, что и global для импорта, затем получить адрес процедуры и настроить его соответствующим образом. Процедуры импорта DLL, с точки зрения Delphi, являются заглушками, которые выполняют косвенный переход через таблицу импорта DLL. Экспортируемые переменные связаны ОС загрузчик, помещая адрес экспортированного глобального в таблицу импорта, почти точно так же, как адреса экспортированных процедур аналогично исправлены.
например:
{$apptype console}
procedure global; external 'exp.dll';
function GetGlobalAddr: PInteger;
type
PPPointer = ^PPointer;
var
p: PByte;
begin
p := @global;
Assert(p^ = $FF); // $FF => indirect jump m32
Inc(p);
Assert(p^ = );
Inc(p);
Result := PPPointer(p)^^
end;
begin
Writeln(GetGlobalAddr^);
end.
конечно, последние детали зависят от реализации и платформы и т. д. Вероятно, более безопасный подход-использовать LoadLibrary
С GetProcAddress
, который вернет адрес глобальной переменной при передаче ее имени. Конечно, это также платформа зависимый.
64-разрядная версия:
в 64-разрядной версии Windows код немного отличается. Опкоды одинаковы, но режим адресации для одной и той же последовательности команд отличается; вместо 32-битного абсолютного смещения это 32-битное относительное смещение.
function GetGlobalAddr: PInteger;
type
PPPointer = ^PPointer;
var
p: PByte;
ofs: Integer;
begin
p := @global;
Assert(p^ = $FF); // $FF => indirect jump m32
Inc(p);
Assert(p^ = );
Inc(p);
// 32-bit offset follows
ofs := PInteger(p)^;
// offset is relative to next instruction
Inc(p, SizeOf(ofs) + ofs);
Result := PPPointer(p)^^
end;
читая документы, я не думаю, что Delphi позволяет экспортировать глобальные переменные напрямую, поскольку справка по инструкции exports обсуждает только подпрограммы. Также есть очень определенное
глобальные переменные, объявленные в общем библиотека не может быть импортирована Delphi приложение.
и, вероятно, можно с уверенностью предположить, что если Delphi не может импортировать их, он также не будет экспортировать их.
Я полагаю, что это может быть экспортировать функцию, которая возвращает указатель на глобальную переменную...
что-то одни строчки:
type
RGlobalRecord = record
...
end;
PGlobalRecord = ^RGlobalRecord;
var
_GlobalRecord: RGlobalRecord;
function GetGlobalRecord: PGlobalRecord;
begin
Result := @_GlobalRecord;
end;
exports GetGlobalRecord name 'ExternalNameOfGlobalRecord';
Итак, если функция NSGetModule возвращает ту же структуру, которую теперь требуется экспортировать как глобальную переменную, вы можете попытаться экспортировать эту функцию с именем, необходимым для глобального VAR для экспорта:
exports NSGetModule name 'NSModule';
вот мое решение Delphi. И это работает даже в д5 :)
function MyComponentConstructor(aOuter: nsISupports; const IID: TGUID; out _result): nsresult; cdecl;
begin
/* constructor */
end;
type
TCIDEntry = record
cid: ^TGUID;
service: Boolean;
getFactoryProc: Pointer;
constructorProc: Pointer;
end;
TContractIDEntry = record
constractid: PChar;
cid: ^TGUID;
end;
TCategoryEntry = record
category: PChar;
entry: PChar;
value: PChar;
end;
TModule = packed record
mVersion: DWord;
mCIDs: array of TCIDEntry;
mContractIDs: array of TContractIDEntry;
mCategoryEntries: array of TCategoryEntry;
getFactoryProc: Pointer;
loadProc: Pointer;
unloadProc: Pointer;
end;
PModule = ^TModule;
PPModule = ^PModule;
var
mCIDs: array [0..1] of TCIDEntry =
(
( cid: @Sample_cid; service: False; getFactoryProc: nil; constructorProc: @MyComponentConstructor ),
( cid: nil; service: False; getFactoryProc: nil; constructorProc: nil )
);
mContractIDs: array [0..1] of TContractIDEntry =
(
( constractid: Sample_CONTRACTID; cid: @Sample_cid ),
( constractid: nil; cid: nil )
);
mCategoryEntries: array [0..2] of TCategoryEntry =
(
( category: 'JavaScript-global-property'; entry: 'MyComponent'; value: Sample_CONTRACTID ),
( category: 'JavaScript-global-constructor'; entry: 'MyComponent'; value: Sample_CONTRACTID ),
( category: nil; entry: nil; value: nil )
);
NSModuleElem: TModule =
(
mVersion: 1;
mCIDs: @mCIDs;
mContractIDs: @mContractIDs;
mCategoryEntries: @mCategoryEntries;
getFactoryProc: nil;
loadProc: nil;
unloadProc: nil
);
NSModule: PModule = Addr(NSModuleElem);
exports
NSModule name 'NSModule';
теперь, если вы можете отправить мне реализацию GenericClassInfo в delphi, это было бы потрясающе:)
Как вы заметили, это не работает в FF 5 и FF 6. Вместо этого вы можете добавить блок инициализации для проверки версии firefox во время выполнения при соответствующей настройке mVersion. Mozilla намеренно нарушает двоичные компоненты, поэтому это работоспособное обходное решение даже между различными версиями.
вы можете использовать приложение.ExeName иhttp://www.delphitricks.com/source-code/files/get_the_version_of_a_file.html
FF 5-mVersion: = 2;
FF 6-mVersion: = 6;
FF 7-mVersion: = 7;
вот моя текущая реализация (это работает в FF 5 и FF 6 и, вероятно, все остальные идут вперед)
type
TCIDEntry = record
CID: PGUID;
Service: BOOL;
GetFactoryProc: Pointer;
ConstructorProc: Pointer;
end;
TContract = record
ContractID: PChar;
CID: PGUID;
end;
TCategory = record
Category: PChar;
Entry: PChar;
Value: PChar;
end;
TModule = record
Version: UINT;
CIDs: Pointer;
Contracts: Pointer;
Categories: Pointer;
GetFactory: Pointer;
Load: Pointer;
Unload: Pointer;
end;
PModule = ^TModule;
var
NSModule: PModule;
implementation
var
mtModule: TModule;
CIDs: array[0..1] of TCIDEntry;
Contracts: array[0..1] of TContract;
function GetFileVersionResourceInfo(const FileName, VerValue: string): string;
var
S: string;
Value: Pointer;
ValueSize: DWORD;
VerInfoSize: DWORD;
VersionInfo: Pointer;
GetInfoSizeJunk: DWORD;
begin
// retrieve the size of the version information resource
VerInfoSize := GetFileVersionInfoSize(PChar(FileName), GetInfoSizeJunk);
if VerInfoSize > 0 then
begin
// retrieve memory to hold the version resource
GetMem(VersionInfo, VerInfoSize);
try
// retrieve the version resource
if GetFileVersionInfo(PChar(FileName), 0, VerInfoSize, VersionInfo) then
if VerQueryValue(VersionInfo, '\VarFileInfo\Translation', Value, ValueSize) then
begin
S := '\StringFileInfo\' +
IntToHex(LoWord(LongInt(Value^)), 4) +
IntToHex(HiWord(LongInt(Value^)), 4) + '\';
if VerQueryValue(VersionInfo, PChar(S + VerValue), Value, ValueSize) then Result := PChar(Value);
end;
finally
FreeMem(VersionInfo, VerInfoSize);
end;
end;
end;
function GetVersion: Integer;
var
I: Integer;
sProductVersion: string;
sModuleFileName: array[0..MAX_PATH] of Char;
begin
Result := 1; // Firefox 4
FillChar(sModuleFileName, MAX_PATH, 0);
if GetModuleFileName(0, sModuleFileName, SizeOf(sModuleFileName)) > 0 then
begin
sProductVersion := Trim(GetFileVersionResourceInfo(sModuleFileName, 'ProductVersion'));
if (sProductVersion <> '') and (sProductVersion[1] in ['4'..'9']) then
begin
// Firefox 4 = version 1
// Firefox 5 = version 2
// Firefox 6 = version 6
// etc.
I := StrToInt(sProductVersion[1]);
if I <= 5 then
Result := I - 3
else
Result := I;
end;
end;
end;
function MyConstructor(aOuter: nsISupports; const aIID: TGUID; out aResult): nsresult; cdecl;
begin
end;
initialization
mtModule.Version := GetVersion;
CIDs[0].CID := @Sample_CID;
CIDs[0].ConstructorProc := @MyConstructor;
mtModule.CIDs := @CIDs;
Contracts[0].ContractID := Sample_CONTRACTID;
Contracts[0].CID := @Sample_CID;
mtModule.Contracts := @Contracts;
NSModule := @mtModule;
end.