Search code examples
delphifirefox-addonxpcom

Porting mozilla's NSModule to Delphi


In extension to this question, I guess I'll best show what I've got so far. What I'm trying to do is create a Firefox extension with Delphi, that'll work with the Firefox versions of the future that will use an exported NSModule structure, and no longer an NSGetModule function.

Main questions I'm struggling with for the moment is:

  • Is the code below correct? I may be wrong with how the pointers and arrays of records work.
  • How to debug this? If I build it and it runs then I'm kind of sure it'll work, but in debugging my library I can only check if my init code does its job. (and for now, Firefox 3.6 doesn't seem to pick up my @mozilla.org/network/protocol;1?name=xxm contract)

The code I'm trying to port is here: http://mxr.mozilla.org/mozilla-central/source/xpcom/components/Module.h

type
  TConstructorProcPtr=function(aOuter:nsISupports;const aIID:TGUID;var aResult:pointer):nsresult;
  TLoadFuncPrt=function:nsresult;
  TUnloadFuncPrt=procedure;
  TCIDEntry=record
    cid:TGUID;
    service:boolean;
    getFactoryProc:pointer;//TGetFactoryProcPtr;
    constructorProc:TConstructorProcPtr;
  end;
  TContractIDEntry=record
    contractid:PChar;
    cid:TGUID;//PGUID?
  end;
  TCategoryEntry=record
    category,entry,value:PChar;
  end;

  TXPCOMModule=packed record
    kVersion:integer;//=1;
    mVersion:cardinal;//kModuleVersion
    mCIDs:^TCIDEntry;//pointer to first in array, last should be nil
    mContractIDs:^TContractIDEntry;//pointer to first in array, last should be nil
    mCategoryEntries:^TCategoryEntry;//pointer to first in array, last should be nil
    getFactoryProcPtr:pointer;//TGetFactoryProcPtr;
    loadProc:TLoadFuncPrt;
    unloadProd:TUnloadFuncPrt;
  end;

Solution

  • You almost certainly need the cdecl calling convention on all your procedure- and function-pointer declarations:

    TConstructorProcPtr = function(aOuter: nsISupports; const aIID: TGUID; var aResult: Pointer): nsresult; cdecl;
    TLoadFuncPrt = function: nsresult; cdecl;
    TUnloadFuncPrt = procedure; cdecl;
    

    I assume you've declared nsISupports as a Delphi interface. Otherwise, you need to make sure the aOuter parameter above is a pointer as it is in the C++ code.

    For TContractIDEntry, and all the other places where you use PChar, I advise you to use PAnsiChar instead. The size of Delphi's Char type changed a couple of years ago, but the C++ char is and always will be one byte, so use Delphi's one-byte character type explicitly. Also, your comment wondering whether to declare the cid field as a PGUID was correct; asterisk means pointer.

    TContractIDEntry = record
      contractid: PAnsiChar;
      cid: PGUID;
    end;
    

    The kVersion field should not be a member of the record you declare. In C++, it's a static member, which means it occupies no space in the structure itself; it's shared by all instances of that type. It's equivalent to a class field in a Delphi class, but I don't think records offer that feature. Make it a unit-level variable instead of a field.