[疑难] xpcom.dll为何不能正常加载?

qiezi 2007-04-13
你搜索一下isCOMclass和vtblOffset这2上,找找相关的源码就看明白了。

那个方案还有个缺陷就是我们用D来写XPCOM组件会有问题
h_rain 2007-04-13
	// Copy vtbl[] from base class
	if (b->base->vtblOffset())
	{   int d = b->base->vtbl.dim;
	    if (d > 1)
	    {
		vtbl.reserve(d - 1);
		for (int j = 1; j < d; j++)
		    vtbl.push(b->base->vtbl.data[j]);
	    }
	}
	else
	{
	    vtbl.append(&b->base->vtbl);
	}


是这吗?j从1开始,跳过了0
qiezi 2007-04-13
噢,是这个地方,看样子要改的话这时也要改了。

它这里是判断vtable有偏移的话,就认为是C++兼容的,直接把虚表拷到一起。否则就是D接口,是用虚表指针数组的。
h_rain 2007-04-13
我刚做了一个DMD源码的doxygen文档,编译为chm还有30多兆,好像不能传上来吧?
有需要的吗?
oldrev 2007-04-13
传你的 doxyfile 就 ok 了
qiezi 2007-04-14
已经可以让它工作了,这是一段测试代码:
void main(char[][] args)
{
    nsIComponentManager componentManager;

    nsresult result;
    result = NS_InitXPCOM2(null, null, null);

    result = NS_GetComponentManager(&componentManager);

    nsILocalFile ifile = null;
    result = componentManager.CreateInstanceByContractID(
        "@mozilla.org/file/local;1",
        null,
        &NS_ILOCALFILE_IID,
        cast(void**)&ifile
        );

    writefln("Create nsIFile instance, result: 0x%08x", result);

    AString str = new AString("~/dxpcom/test.d"w);
    result = ifile.InitWithPath(cast(nsAString*)str);
    writefln("InitWithPath, result: 0x%08x", result);

    long filesize;
    result = ifile.GetFileSize(&filesize);
    writefln("GetFileSize result: 0x%08x, filesize: %d", result, filesize);

    result = NS_ShutdownXPCOM(null);
    writefln(result);
}

目前使用的方案是前面所说的,先把QueryInterface从接口中拿掉。xpcom里面用的字符串类nsAString和nsACString使用比较麻烦,这2个类实际上是傀儡,内部有另外的实现,而且涉及到内存管理,所以用了一个D的AString和ACString类来包装它。由于D有GC,所以不用再去处理显式内存管理了,AString的析构被调用时会执行XPCOM字符串的内存释放。

上面这个例子会调用nsILocalFile,输出我用来测试的test.d文件的大小,结果是正确的。当然上面的代码没有处理XPCOM对象的一些引用计数,只是用来测试的。

接口类目前手工转了一些必要的,而且很容易出错,导致我今天调试了很我,比如漏掉一个方法,虚表错位导致调用失败等。所以目前要停一下,先修改xpidl代码,增加一个到D语言的转换。本来上次已经做了一点,不过不小心给删了。
qiezi 2007-04-14
还应该处理一个东西,就是原始C++接口的XPCOM往D接口上转,否则使用起来还是很难受的,比如返回值应该处理成异常,返回参数移回到返回值上,不知道能否实现,不过肯定会保留一个与C++接口相容的原始接口,D接口另行封装。
qiezi 2007-04-14
从C++接口转到D包装类还是有点麻烦的,首先从入口开始,比如是NS_GetComponentManager,先写一个D版本的:

IComponentManager GetComponentManager()
{
  // ...
}

这个IComponentManager是D包装类,C++版本的是nsIComponentManager。上面这个方法会处理NS_ComponentManager的返回值,如果不是0则抛出异常,从返回值体系转到异常体系。

下一步就是QueryInterface:
ILocalFile ifile = cast(ILocalFile)componentManager.QueryInterface(ILocalFile.IID);

这里稍有点麻烦的是要根据IID正确转换返回值类型,因为原始的QueryInterface给我们的可能是nsILocalFile,需要查找一个表来把它转换到ILocalFile类型,这个表只能根据IID来查。

这里返回值同样被包装成异常。当然ILocalFile这个D类的各个方法也都是完成它的C++版本接口的成员的调用,使用时完全不用接触C++风格接口。

至于为什么要进行这么“厚”的包装,原因很简单,用异常体系比返回体系更容易编写代码,开发更快,损失的效率是比较少的。另外包装也是为了把C++的接口风格拉回到IDL接口风格。

用D编写XPCOM组件暂时还不支持,虚表有点小问题,上面这种异常风格到返回值风格的逆向转换还没想好,应该也是可行的,要多生成一套代码出来了。
h_rain 2007-04-15
我觉得
ILocalFile ifile = cast(ILocalFile)componentManager.QueryInterface(ILocalFile.IID);


完全可以写为
ILocalFile ifile = componentManager.QueryInterface(ILocalFile);
或
auto ifile =componentManager.QueryInterface(ILocalFile);

或其他类似的形式吧,使用模板在D的封装层上。

要不还是使用D仅仅对c++接口进行“模拟”,再另外提供工具类模板?
因为ILocalFile接口是我们想要的,而在使用的语法上,出现一次就应该够了。

D现在将Windows的COM接口模型与windows stdcall调用约定放在一起了(对于D中接口),现在看我们需要的是,调用约定是 windows stdcall,而同时接口模型是C++标准接口。
这两样东西其实应该是分开的,接口模型与调用约定。
如果分开的话,在D中应该怎么表示? extern "Windows","CppInterface"?

qiezi学长干得真不错!赞~
qiezi 2007-04-15
这样不可以的,ILocalFile是个类型,不能作为参数值。。

目前IDL到D接口转换的工具我已经修改好了,还要做一个IDL到D包装类的转换,也就是上面说的异常风格包装代码
Global site tag (gtag.js) - Google Analytics