我正在尝试在Delphi中创建一个与Gecko 2.0兼容的DLL。
以前(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
我的问题:我如何在Delphi中实现这一目标?如何导出全局变量?
感谢您的反馈意见。
答案 0 :(得分:14)
Delphi以类似于导出函数的方式从DLL导出全局变量:
library exp;
var
global: Integer;
exports global;
end.
Delphi可以从DLL导入全局变量,但这有点像黑客攻击:声明一个与要导入的全局同名的DLL导入过程,然后获取过程的地址并适当地调整它。从Delphi的角度来看,DLL导入的程序是通过DLL导入表间接跳转的存根。通过将导出的全局地址放入导入表中,导出的变量由OS加载程序链接,几乎与导出过程的地址类似地修补的方式相同。
例如:
{$apptype console}
procedure global; external 'exp.dll';
function GetGlobalAddr: PInteger;
type
PPPointer = ^PPointer;
var
p: PByte;
begin
p := @global;
Assert(p^ = $FF); // $FF $25 => indirect jump m32
Inc(p);
Assert(p^ = $25);
Inc(p);
Result := PPPointer(p)^^
end;
begin
Writeln(GetGlobalAddr^);
end.
当然,后者的细节是实现和平台相关的等等。可能更安全的方法是使用LoadLibrary
和GetProcAddress
,它将在传递其名称时返回全局变量的地址。当然,这也取决于平台。
64位更新:
在Windows上64位,代码略有不同。操作码相同,但相同指令序列的寻址模式不同;而不是32位绝对偏移,它是32位相对偏移。
function GetGlobalAddr: PInteger;
type
PPPointer = ^PPointer;
var
p: PByte;
ofs: Integer;
begin
p := @global;
Assert(p^ = $FF); // $FF $25 => indirect jump m32
Inc(p);
Assert(p^ = $25);
Inc(p);
// 32-bit offset follows
ofs := PInteger(p)^;
// offset is relative to next instruction
Inc(p, SizeOf(ofs) + ofs);
Result := PPPointer(p)^^
end;
答案 1 :(得分:2)
阅读文档,我认为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';
答案 2 :(得分:1)
这是我的Delphi解决方案。它甚至可以在D5中运行:)
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';
现在,如果您可以在delphi中向我发送GenericClassInfo实现,那将是非常棒的:)
答案 3 :(得分:0)
正如您所注意到的,这在FF 5和FF 6中不起作用。相反,您可以添加初始化块以在运行时检查firefox版本,相应地调整mVersion。 Mozilla故意破坏二进制组件,因此即使在不同版本之间也是如此。
您可以使用Application.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;
答案 4 :(得分:0)
这是我当前的实现(这适用于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.