[疑难] 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包装类的转换,也就是上面说的异常风格包装代码 |