我在DLL中建立了一个TImage,但调用时出现错误"Cannot assign a TFont to a TFont"

我在DLL中建立了一个TImage,但调用时出现错误"Cannot assign a TFont to a TFont" Delphi / Windows SDK/API
http://www.delphi2007.net/DelphiAPI/html/delphi_20061118180523201.html
DLL中的函数:  
  Function   InitImage(tar:TWinControl,fn);  
          oSelImg:=TImage.Create(nil);  
          oSelImg.AutoSize:=true;  
          oSelImg.Picture.LoadFromFile(fn);  
          oSelImg.Parent:=tar;        
          oSelImg.Show;  
   
  主程序调用时,   也能成功将图片显示在窗体上,但始终会弹出  
  "Cannot   assign   a   TFont   to   a   TFont"错误,   应如何解决呢?  
 

用Package代替DLL。

不想用Package

深度拷贝的问题,用包吧!

传说不用包也行,传个APPLICATION即可,但细节我不清楚

oSelImg.ParentFont   :=   False;   //   不触发Font.Assign()//   加上看看  
  oSelImg.Parent   :=   tar;

四星清洁工果然是第一牛人,向清洁工致敬!  
  不过由于TImage的ParentFont(派生于TControl)没有public   或   published,所以对TImage稍微做点工作。我的测试如下:  
   
  type  
      TMyImage   =   class(TImage)  
      private  
      protected  
      public  
      published  
          property   ParentFont;    
      end;  
   
  {$R   *.res}  
   
  procedure   InitImage(AParent:   TWinControl;   AFileName:   string);  
  var  
      oSelImg:   TMyImage;  
  begin  
      oSelImg:=TMyImage.Create(nil);  
      oSelImg.AutoSize:=true;  
      oSelImg.Picture.LoadFromFile(AFileName);  
      oSelImg.ParentFont   :=   False;  
      oSelImg.Parent   :=   AParent;  
      oSelImg.Show;  
  end;  
   
  exports  
      InitImage;  
  end.  
   
  ....测试  
   
  var  
      Form1:   TForm1;  
      procedure   InitImage(AParent:TWinControl;   AFileName:   string);   external   'Image.dll';  
   
  implementation  
   
  {$R   *.dfm}  
   
  procedure   TForm1.Button1Click(Sender:   TObject);  
  begin  
      InitImage(Self,   'User.bmp');  
  end;  
   
  一切如愿。    
   
  PS:   我把这个问题的症结阐述转贴一下,希望对类似问题都有清晰的认识  
   
  The   DLL   will   not   use   the   same   classes   as   the   main   program   even   when   compiled   from   the   same   source.   Objects   will   look   the   same,   but   their   classes   will   not   compare   equal.   That's   why   assigning   a   TFont   value   from   the   DLL   to   a   TFont   property   in   the   main   program   (or   the   other   way   around)   doesn't   work:   the   Assign   procedure   is   looking   for   the   main   program's   TFont   class,   and   never   recognises   the   DLL's   TFont   object.    
   
  再次谢谢清洁工为我们指明道路。

让那些笨蛋都看看,这才是真正的高手!!!  
   
   
  zswang(伴水清清)(专家门诊清洁工)   ,sanmaotuo(老冯):  
  如果你们想业余时间接点私活的话,请留下联系方式.

楼主不厚道

没有所谓高手,也没有所谓笨蛋,人各有所长  
  这个问题是你自己的问题  
  大家花时间帮你想办法  
  无论解决与否,你都该心存感激  
  做人要厚道

谢谢你的提醒。  
  偶也是因为太厚道了,被人骗了几K

老冯,经常潜水看你的回帖,把你的MSN告诉我可以么?我的是zhiliposui8@hotmail.com

问最后一个问题,然后立刻结贴。  
   
  DLL中这里procedure   InitImage(AParent:   TWinControl;   AFileName:   string);  
  如果我将TWinControl换成HWND,如  
  procedure   InitImage(AParent:   HWND;   AFileName:   string);  
  应如何实现同样功能?  
  (CALLER也是HWND)

呵呵,老冯来了  
   
  通过  
   
  function   FindControl(Handle:   HWnd):   TWinControl;  
   
  没有问题吧。

注意function   FindControl(Handle:   HWnd):   TWinControl是需要修正的哈。否则......

aiirii走后,zswang(伴水清清)(专家门诊清洁工)   成为D版第一牛人了,能否帮在下解决一下http://community.csdn.net/Expert/topic/5133/5133079.xml?temp=.3957483中的问题?

to:老冯:  
  请问如何修正?

哈哈哈哈,等了你好几天了,鲨鱼终于浮面了。  
  我把整个Dll再贴一次,注意里面的_FindControl函数  
   
      TMyImage   =   class(TImage)  
      private  
      protected  
      public  
      published  
          property   ParentFont;  
      end;  
   
  var  
      ControlAtom:   TAtom;  
      ControlAtomString:   string;  
      RM_GetObjectInstance:   DWORD;//   registered   window   message  
   
  procedure   _InitControls;  
  begin  
      ControlAtomString   :=   Format('ControlOfs%.8X%.8X',   [GetModuleHandle(nil),   GetCurrentThreadID]);  
      ControlAtom   :=   GlobalAddAtom(PChar(ControlAtomString));  
      RM_GetObjectInstance   :=   RegisterWindowMessage(PChar(ControlAtomString));  
  end;  
   
  function   _FindControl(Handle:   HWnd):   TWinControl;  
  var  
      OwningProcess:   DWORD;  
  begin  
      Result   :=   nil;  
      if   (Handle   <>   0)   and  
            (GetWindowThreadProcessID(Handle,   OwningProcess)   <>   0)   and  
            (OwningProcess   =   GetCurrentProcessId)   then  
      begin  
          if   GlobalFindAtom(PChar(ControlAtomString))   =   ControlAtom   then  
              Result   :=   Pointer(GetProp(Handle,   MakeIntAtom(ControlAtom)))  
          else  
              Result   :=   Pointer(SendMessage(Handle,   RM_GetObjectInstance,   0,   0));  
      end;  
  end;  
   
  procedure   InitImage(AParent:   HWND;   AFileName:   string);  
  var  
      oSelImg:   TMyImage;  
  begin  
      oSelImg:=TMyImage.Create(nil);  
      oSelImg.AutoSize:=true;  
      oSelImg.Picture.LoadFromFile(AFileName);  
      oSelImg.ParentFont   :=   False;  
      oSelImg.Parent   :=   _FindControl(AParent);  
      oSelImg.Show;  
  end;  
   
  exports  
      InitImage;  
   
  {$R   *.res}  
   
  begin  
      _InitControls;  
  end.

至于为什么要修正原来的FindControl,里面可有学问了。

具体我不清楚为什么,  
  但看到似乎要为DLL专门建立一个消息以得到当前进程ID等信息为POINTER使用.  
   
  再次感谢你!  
  交个朋友:topduan@hotmail.com

不客气,通过解决问题和技术交流我也学到了不少东西。特别从四星清洁工这样的优秀程序员那里学到了书本上学不到的很多技巧和思路。

临时又遇到个问题,  
  为什么我在DLL中是返回PCHAR的,  
  但在主程序中得到的有时是乱码,有时又正常:  
   
  DLL中:  
  GetByField1(...):pchar;stdcall;  
  begin  
      s:=ado1.Fields[0].asstring;  
      result:=PChar(s);  
   
   
  主程序中:  
  var   tmp:pchar;  
   
      tmp:=GetByField1(-1,pchar('Type'));  
  showmessage(tmp);         //这里有时会得到乱码.    
   
  什么原因?如何解决  
 

呵呵。Type是系统保留字,你不能直接PChar('Type')。如果需要这个字符串,你这样做:  
  var  
  Str:   string;  
  Tmp:   PChar;  
  begin  
      _String   :=   'Type';  
      Tmp   :=   GetByField1(-1,PChar(Str));  
      ShowMessage(Tmp);    
      ......  
  end;  
   
  至于为什么会这样,你可以仔细看看DELPHIBasis教程(英国DELPHI学院出的)中关于指针以及内存存活的详细说明    
 

对不起,上面有误

我把Type改了另外一个名字,但仍然得到乱码有时...

这个可能要四星清洁工来解释了。

呼唤4星清洁工~~~~

mark!

(*   参考标准API写接口   *)  
  我们常用的api函数,如:GetClassName(),GetWindowText()...  
  var  
      vBuffer:   PChar;  
  begin  
      GetMem(vBuffer,   256);  
      GetClassName(Handle,   vBuffer,   256);  
      Caption   :=   vBuffer;  
      FreeMem(vBuffer,   256);  
  end;  
  (*   App和Dll使用内存有一个原则,就是:谁分配的资源由谁来释放   *)  
  在调用的时候你都必须先分配好资源  
  为什么系统Api函数要这样做?不直接返回一个PChar给你  
  因为:给你一个PChar,那这个PChar的内存资源由谁释放?  
  系统怎么知道你什么时候使用完毕?  
   
  (*   string的内存管理机制很复杂   *)  
  pascal里的string很好用,但是简单的背后是复杂的内存管理机制  
  var  
      S:   string;  
  begin  
      S   :=   '1234';  
      //   实际上这里编译器会释放S的内存空间,asm:   call   @LStrClr  
  end;  
   
  (*   函数调用完毕后,局部变量会被释放   *)  
  想也想得到:  
  成千上万的函数模块如果不是动态给局部变量分配内存资源  
  那么会有多大的内存浪费  
   
  简单来说:  
  〉GetByField1(...):pchar;stdcall;  
  〉begin  
  〉     s:=ado1.Fields[0].asstring;  
  〉     result:=PChar(s);  
  这里的s如果是局部变量,在调用完GetByField1后资源就被释放了  
   
  〉主程序中:  
  〉var   tmp:pchar;  
  〉     tmp:=GetByField1(-1,pchar('Type'));  
  〉     showmessage(tmp);         //这里有时会得到乱码.    
  tmp得到的实际上是一个已经被释放掉资源的垃圾空间,如果有被其他地方收回使用那么这段空间什么内容都可能有  
   
  最后:你就参考GetClassName这样的调用方式,改接口吧

既然专家在此,就再请教一下:  
  如何增强调用DLL的程序的稳固性?  
  目前所做的程序,调用的DLL中包含有个窗体,  
  有时此DLL出错,该错误反弹到主程序中,直接导致主程序崩溃.即使两者代码中都TRY...EXCEPT或FINALLY也无济于事.  
  应如何解决类似问题???

LZ的性欲不高啊

//LZ的性欲不高啊  
   
  呵呵呵呵,LZ是做批发的。多多益善。少花钱,多办事,符合国情。

一路看下来   LZ还真不厚道    
  不过爱学习到是值得赞扬的   没想到老冯的水平比我想象的还要高    
  清洁工的编程思路的确高   不愧是D版第一牛人啊   可敬   ~~    
 

没办法,我很穷,在CSDN还剩5分.  
  谁要,谁拿去吧

TO:老冯:  
  真是头大了!  
  主程序调用有数据库组件(无窗体)的DLL时,有时提示错误"Access   violation   at   address   0000000.   Read   of   address   0000000"有时又没有.  
  怎么都找不到原因,   到底怎么回事????  
   
  好人做到底吧...  
   
 

没关系,上面都是开玩笑的。老话说“给咖啡加点糖".呵呵呵呵  
  "Access   violation   at   address   0000000.   Read   of   address   0000000"  
  这个是内存违规的意外信息,原因有很多很多。但一般来说都是调用没有实例化的对象造成的。  
  如果你是有时的话,那你一般是销毁了某个实例而后又去调用它。反正很多原因,没有看代码确实不好诊断。  
 

我刚找到原因:  
  我在DLL中用到了  
  ado.RecordCount   =0  
  和  
  ado.RecNo:=1;  
  程序执行到这里,有时会出错!!!   为什么?!!!  
   
  这里:  
      ado:=TADODataSet.Create(nil);  
 

自己都觉得不好意思了,问那么多问题  
  但这个目前确实很头疼,在大富翁里也未找到答案,  
  再次真诚感谢,这部分问题解决立即给分

up  
  ...  
  ..

posted on 2009-02-03 09:44  delphi2007  阅读(481)  评论(0编辑  收藏  举报