Thread overview
extern(Windows) on XPCOM
Apr 13, 2007
Li Jie
Apr 21, 2007
Li Jie
April 13, 2007
I want to call XPCOM and write XPCOM component with D, but I have some problems.

This is COM object virtual table structure:
# COM:
# vtbl -> | a_pointer (I don't know what is this)
#         | QueryInterface
# 	| AddRef
# 	| Release

And this is XPCOM object virtual table:
# XPCOM:
# vtbl -> | QueryInterface
# 	| AddRef
# 	| Release

extern(Windows) only compatible COM, because it added an offset, XPCOM do not need this.
When call QueryInterface, it really call AddRef.
And I tried extern(C++) and extern(Pascal), no changes.

I think that there are 4 solutions:

1. Don't use interface, replace with struct, C style:
# struct nsISupportsVtable
# {
# extern(Windows) :
#     nsresult function (nsISupports*, nsIID* uuid, void** result) QueryInterface;
#
#     nsrefcnt function(nsISupports*) AddRef;
#
#     nsrefcnt function(nsISupports*) Release;
# }
#
# struct nsISupports
# {
#     nsISupportsVtable* vtbl;
# }

Ugly! but it can works.


2. Modify XPCOM interface, remove QueryInterface method:
# extern(Windows)
# interface nsISupports
# {
#     // Remove it from D interface
#     // nsresult QueryInterface(nsIID* uuid, void** result);
#
#     nsrefcnt AddRef();
#
#     nsrefcnt Release();
# }
#
# private
# struct VTBL
# {
#     extern(Windows) nsresult function (nsISupports, nsIID*, void**) QueryInterface;
# }
#
# private
# struct ISupports
# {
#     VTBL* vtbl;
# }
#
# nsresult MyQueryInterface(nsISupports obj, nsIID* iid, void** pout)
# {
#     ISupports* p = cast(ISupports*)cast(void*)obj;
#     return p.vtbl.QueryInterface(obj, iid, pout);
# }

It can works, replace nsISupports.QueryInterface with MyQueryInterface. But I can't write XPCOM component with D, because no QueryInterface in interface.


3. Hack DMD:
# int InterfaceDeclaration::vtblOffset()
# {
#     if (isCOMclass())
# 	return 0;
#     return 1;
# }

Change to:

# int InterfaceDeclaration::vtblOffset()
# {
#     return 0;
# }

And add pointer to IUnknown:
# extern(Windows)
# interface IUnknown
# {
#     void ___dont__call__me__please_______();
#     HRESULT QueryInterface(IID* riid, void** pvObject);
#     ULONG AddRef();
#     ULONG Release();
# }

I don't know whether it can work.

4. Hack DMD, add extern(XPCOM), I think it's very difficult to do.


Any ideas?
April 13, 2007
Li Jie wrote:
> I want to call XPCOM and write XPCOM component with D, but I have some problems.
> 
> This is COM object virtual table structure:
> # COM:
> # vtbl -> | a_pointer (I don't know what is this)
> #         | QueryInterface
> # 	| AddRef
> # 	| Release
> 
> And this is XPCOM object virtual table:
> # XPCOM:
> # vtbl -> | QueryInterface
> # 	| AddRef
> # 	| Release
> 
> extern(Windows) only compatible COM, because it added an offset, XPCOM do not need this.
> When call QueryInterface, it really call AddRef.
> And I tried extern(C++) and extern(Pascal), no changes.
> 
> I think that there are 4 solutions:
> 
> 1. Don't use interface, replace with struct, C style:
> # struct nsISupportsVtable  # {  # extern(Windows) :  #     nsresult function (nsISupports*, nsIID* uuid, void** result) QueryInterface;  #       #     nsrefcnt function(nsISupports*) AddRef;  #       #     nsrefcnt function(nsISupports*) Release;  # }  #   # struct nsISupports  # {  #     nsISupportsVtable* vtbl;  # }
> 
> Ugly! but it can works.
> 
> 
> 2. Modify XPCOM interface, remove QueryInterface method:
> # extern(Windows)  # interface nsISupports  # {  #     // Remove it from D interface  #     // nsresult QueryInterface(nsIID* uuid, void** result);  #   #     nsrefcnt AddRef();  #   #     nsrefcnt Release();  # }  #
> # private  # struct VTBL  # {  #     extern(Windows) nsresult function (nsISupports, nsIID*, void**) QueryInterface;  # }  #   # private  # struct ISupports  # {  #     VTBL* vtbl;  # }  #   # nsresult MyQueryInterface(nsISupports obj, nsIID* iid, void** pout)  # {  #     ISupports* p = cast(ISupports*)cast(void*)obj;  #     return p.vtbl.QueryInterface(obj, iid, pout);  # } 
> 
> It can works, replace nsISupports.QueryInterface with MyQueryInterface. But I can't write XPCOM component with D, because no QueryInterface in interface.
> 
> 
> 3. Hack DMD:
> # int InterfaceDeclaration::vtblOffset()
> # {
> #     if (isCOMclass())
> # 	return 0;
> #     return 1;
> # }
> 
> Change to:
> 
> # int InterfaceDeclaration::vtblOffset()
> # {
> #     return 0;
> # }
> 
> And add pointer to IUnknown:
> # extern(Windows)
> # interface IUnknown
> # {
> #     void ___dont__call__me__please_______();
> #     HRESULT QueryInterface(IID* riid, void** pvObject);
> #     ULONG AddRef();
> #     ULONG Release();
> # }
> 
> I don't know whether it can work.
> 
> 4. Hack DMD, add extern(XPCOM), I think it's very difficult to do.
> 
> 
> Any ideas?

I would think a merge of options 3. and 4. would be good.  Ie, add either a second interface (IXPUnknown perhaps) or an extern(XPCOM), and modify DMD's InterfaceDeclaration::vtblOffset to know about the special case.  It'd be Walter's choice, and I'm not so sure XPCOM would be anywhere near the top of his priority list.

*ponder ponder*

-- Chris Nicholson-Sauls
April 21, 2007
Chris Nicholson-Sauls дµ½:

> 
> Li Jie wrote:
> > I want to call XPCOM and write XPCOM component with D, but I have some problems.
> > 
> > This is COM object virtual table structure:
> > # COM:
> > # vtbl -> | a_pointer (I don't know what is this)
> > #         | QueryInterface
> > # 	| AddRef
> > # 	| Release
> > 
> > And this is XPCOM object virtual table:
> > # XPCOM:
> > # vtbl -> | QueryInterface
> > # 	| AddRef
> > # 	| Release
> > 
> > extern(Windows) only compatible COM, because it added an offset, XPCOM do not need this.
> > When call QueryInterface, it really call AddRef.
> > And I tried extern(C++) and extern(Pascal), no changes.
> > 
> > I think that there are 4 solutions:
> > 
> > 1. Don't use interface, replace with struct, C style:
> > # struct nsISupportsVtable
> > # {
> > # extern(Windows) :
> > #     nsresult function (nsISupports*, nsIID* uuid, void** result) QueryInterface;
> > #
> > #     nsrefcnt function(nsISupports*) AddRef;
> > #
> > #     nsrefcnt function(nsISupports*) Release;
> > # }
> > #
> > # struct nsISupports
> > # {
> > #     nsISupportsVtable* vtbl;
> > # }
> > 
> > Ugly! but it can works.
> > 
> > 
> > 2. Modify XPCOM interface, remove QueryInterface method:
> > # extern(Windows)
> > # interface nsISupports
> > # {
> > #     // Remove it from D interface
> > #     // nsresult QueryInterface(nsIID* uuid, void** result);
> > #
> > #     nsrefcnt AddRef();
> > #
> > #     nsrefcnt Release();
> > # }
> > #
> > # private
> > # struct VTBL
> > # {
> > #     extern(Windows) nsresult function (nsISupports, nsIID*, void**) QueryInterface;
> > # }
> > #
> > # private
> > # struct ISupports
> > # {
> > #     VTBL* vtbl;
> > # }
> > #
> > # nsresult MyQueryInterface(nsISupports obj, nsIID* iid, void** pout)
> > # {
> > #     ISupports* p = cast(ISupports*)cast(void*)obj;
> > #     return p.vtbl.QueryInterface(obj, iid, pout);
> > # }
> > 
> > It can works, replace nsISupports.QueryInterface with MyQueryInterface. But I can't write XPCOM component with D, because no QueryInterface in interface.
> > 
> > 
> > 3. Hack DMD:
> > # int InterfaceDeclaration::vtblOffset()
> > # {
> > #     if (isCOMclass())
> > # 	return 0;
> > #     return 1;
> > # }
> > 
> > Change to:
> > 
> > # int InterfaceDeclaration::vtblOffset()
> > # {
> > #     return 0;
> > # }
> > 
> > And add pointer to IUnknown:
> > # extern(Windows)
> > # interface IUnknown
> > # {
> > #     void ___dont__call__me__please_______();
> > #     HRESULT QueryInterface(IID* riid, void** pvObject);
> > #     ULONG AddRef();
> > #     ULONG Release();
> > # }
> > 
> > I don't know whether it can work.
> > 
> > 4. Hack DMD, add extern(XPCOM), I think it's very difficult to do.
> > 
> > 
> > Any ideas?
> 
> I would think a merge of options 3. and 4. would be good.  Ie, add either a second interface (IXPUnknown perhaps) or an extern(XPCOM), and modify DMD's InterfaceDeclaration::vtblOffset to know about the special case.  It'd be Walter's choice, and I'm not so sure XPCOM would be anywhere near the top of his priority list.

My friend, h_rain has found a simple way, rename nsISupports to IUnknown, like this:

---------------------------------------
extern(Windows)
interface IUnknown {
  // some methods
}

alias IUnknown nsISupports;

extern(Windows)
interface nsIFile : nsISupports {
  // some methods
}
---------------------------------------

It works fine, and it is beautiful.