http://topic.csdn.net/u/20090225/09/55d0c305-57e4-4b27-a7e6-40979c8274e4.html
一直都搞不明白,为啥在dll创建对象,并把对象地址返回给主调后,dll中的RTTI与主调的RTTI地址不一致会造成会造成失败?
一直比较疑惑
也看了system.isclass或inheritedfrom等函数,但还是不清楚,请赐教!

解决方案 »

  1.   

    在dll中写一个返回对象的函数与在主调中写一个返回对象的函数不一样?
    共用内存管理器是不是就可以解决这种RTTI转换问题???
      

  2.   

    Delphi在处理RTTI信息时依赖于类型库的类型树信息.
    假定有A和B两个DLL,分属两个人开发,那么就极有可能各自都定义了一个类TMyClass,如果说编译器不把他们区分看待的话,极有可能会导致冲突,并且这个冲突所带来的麻烦是无可估量的.所以Delphi编译器就把这种类型的检查做到了模块的相对地址,从而由编译器在编译的时候进行相对地址调整,从而保证了各模块的类型信息的独立性.就好比A和B来自不同的城市,两个人都住在中山北路X号,于是他们对外人都会公司一个地址"中山北路X号",对于外人来讲,如果是直接由他们各自自己带路肯定不会存在问题,但是,如果是A跟你说的"中山北路X号",然后让B给你带路,那么就会产生一个不匹配的现象,于是B就会查看你提供的地址信息,然后看到城市不匹配,于是告诉你找不到.类型库亦是如此,当被编译之后存在于两个模块当中,他们的基地址(模块基地址)是不同的,就好比来自于两个城市.此问题除非共享类型库,即A和B模块的类型库都来源于MyClass.DLL/MyClass.bpl,才能够保证他们的一致性.Share Memory Manager的概念跟这个相同,只是他们分管的内容不同。内存管理器只是保证内存的申请和释放的一致性,但是并不保证同名类型库指示同一内容,这正如前面说的,A和B都有可能定义TMyClass这样一个类型一样。对于公开的类型来讲,可以通过naming space来解决,但是对于二进制的黑匣子DLL/BPL来讲,兼容性无法保证的情况下,那么这个黑匣子本身就应当被当作为一个独立的naming space.
      

  3.   

    如果你把一个类型,按照Module Full Path + Class name作为一个类型的全名的话,也许会比较好理解,也就是模块的完整路径即为该类型的命名空间,或者称之为前缀。
      

  4.   

    还是没搞明白~
    把dll模块装载到exe中之后,虽然dll的地址是相对的,但应该是共享堆空间的话,即然共享堆空间,数据结构就应该一致!
    而僵哥您说的这个,好像是把2个模块的地址是看成是独立的,把dll中的对象地址传到主调中,在转型时是用主调的地址来转造成的问题!
      

  5.   

    简单来说,模块A有模块A的rtti,模块B有模块B的rtti,它们都在pe文件的code段,也就是运行期的代码中(所以才叫 Run-Time Type Info)
    但是delphi对类/对象的识别只对地址进行判断,比如
    class function TObject.InheritsFrom(AClass: TClass): Boolean;
    var
      ClassPtr: TClass;
    begin
      ClassPtr := Self;
      while (ClassPtr <> nil) and (ClassPtr <> AClass) do
        ClassPtr := PPointer(Integer(ClassPtr) + vmtParent)^;
      Result := ClassPtr = AClass;
    end;
    它的逻辑非常清楚,如果当前的 ClassPtr 和 AClass 不同的话,则令 ClassPtr 等于其父类的地址再重复判断,直到没有父类为止或两者地址相等为止。而这些信息都是写在rtti中的。
    很显然的是,一个进程中的两个模块不可能拥有相同的地址,所以两个模块中的rtti的地址自然也就不同。exe中的class的父类指针如果没有人为干预的话,是不可能指向dll的代码区的出错的话,一般也是调用了基于类的地址判断并引发异常的函数,比如as运算符(System._AsClass)中就会引发一个异常。也就是说,不是一定会出现异常,但是除非你完整的看过某个class的实现,不然没法保证没有使用会引发异常的代码
      

  6.   

    理解了,谢谢Seamour 、僵哥