• DLL对象类型转换


    //以下代码是错误的!!!
    //这一节主要告诉大家,以这种方式进行开发dll是不对的以及错误原因,正确的方式是什么!
    //DLL内创建对象,并把对象返回
    function GetDataSet(str,conn:PChar): TADODataSet;stdcall; 
    begin 
      Result:=TADODataSet.Create(nil); 
      Result.Close; 
      Result.ConnectionString:=conn; 
      Result.CommandText:= str; 
      try 
          Result.Open; 
      except 
        on E:Exception do 
        begin 
            ShowMessage(E.Message); 
            GetDataSet:=nil; 
        end; 
      end; 
    end; 
    //主调程序
    var 
      datas:TADODataSet; 
      sql,conn:string; 
      i:Integer; 
    begin 
      sql:='select ....'; 
      conn:='Provider=SQLOLEDB.1;Password=sa;Persist Security Info=True;User ID=sa;Initial Catalog=Master'; 
        datas:=GetDataSet(PChar(sql),PChar(conn));//如果单纯返回一个对象,这样没问题,但要注意的是不要进行类型转换(这里的类型转换包括显式或隐式类型转换)
        try 
            DBGridEh1.DataSource.DataSet:=datas; 
    //上面这句话就出错了,原因如下:
    //导致错误的原因为RTTI转换导致地址不一致的问题!
    dbgrid.datasource.dataset:=datas;
    datas是TADODataset类型,由于是一个的地址是从dll传出至主调中,在赋值前是一个隐式的向上转型的过程(Dataset<--TADODataSet),这时会调用Is 、As等System单元的函数,仔细看一下Is与As的实现(IsClass、InheritedFrom),不难发现在转换过程中把dll中的RTTI转换为application中的RTTI,造成地址不一致,转换错误!
    为什么地址不一致呢?
    因为dll与exe维护着各自的的RTTI表,位于PE文件头下方的的Code段(不清楚的朋友可看一下Win32的进程布局),如主调与dll中都有Dataset对象,在进程装载时先将主进程中的RTTI载入,地址为ox00611000,而dll载入时dll内的dataset类层次不可能为ox00611000,可能是ox00722000,而这时在主调或dll中转换时,Delphi是对RTTI中记录的各个类进行地址判断,如果相同,则直接将地址赋值,如果不同,则递归性地找其父类地址,如果其父类地址还是找不到,则会提示错误,很显然,地址肯定是不一样的!
    解决方法:
    如果实在想共享对象,并有可能在主调或dll中进行转型怎么办?
    答:用bpl,当使用bpl时,bpl与主调共用一个RTTI,这样就没有转型的问题了!
        except 
          on E:Exception do 
          begin 
            ShowMessage(E.Message); 
          end; 
      end; 
    View Code
  • 相关阅读:
    paxos算法
    List
    es资料汇总
    尚硅谷Kafka
    lostach安装配置
    zookeeper安装
    zookeeper配置详解
    C# 微信企业付款给个人之相关配置
    JS--正则表达式验证
    uniapp小程序--自定义分享标题
  • 原文地址:https://www.cnblogs.com/key-ok/p/3358871.html
Copyright © 2020-2023  润新知