Экспорт глобального символа из библиотеки 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.