导航:
论坛 -> DELPHI技术
斑竹:liumazi,sephil
作者:
litou (泥头)
★☆☆☆☆
-
盒子活跃会员
2012/10/18 17:44:15
标题:
加入我的收藏
楼主:
一程序,有主窗体和一工作线程,现两者间通过消息传递内容并执行,工作线程的消息循环已经建立,主窗体postthreadmessage到工作线程,工作线程postmessage到主窗体,由于相互传输的数据根据功能不同而不同,故不能统一传输格式,只能根据实际的数据创建不同的类(dto)来传输,举个例子 主窗体登陆:在登陆事件中创建登陆数据类局部变量TLoginData,并用postthreadmessage发送消息,消息中wparam是Integer(@LoginData) 工作线程接收时还原类PLoginData(Msg.wparam)^便取得相应的数据,使用后在工作线程free掉 实际过程中发现在线程中有时获取成功有时不行,并且报内存错误,难道是创建类示例的是局部变量,退出登陆函数后局部变量被系统自动回收?尽管变量被回收,但指针指向的数据没有被free掉,在线程中应该也可以获取吧? 用sendmessage肯定没问题,但程序需要只能用postmessage,否则线程处于等待状态。
----------------------------------------------
-
作者:
leozem (leozem)
★☆☆☆☆
-
盒子活跃会员
2012/10/18 18:06:13
1楼:
TLoginData用局部变量,有可能在线程中还没接收完就被释放了。 TLoginData定义成指针,PLoginData = ^TLoginData; var lpLoginData : PLoginData; begin New(PLoginData); PLoginData^....... ........ 赋值操作 postmessage。。 wparam是Integer(lpLoginData ) end; 接收: var lpLoginData : PLoginData; begin lpLoginData := PLoginData(Msg.wparam); 操作 最后释放掉 Dispose(lpLoginData); end; 更好的方法楼下补充。。。
----------------------------------------------
-
作者:
litou (泥头)
★☆☆☆☆
-
盒子活跃会员
2012/10/18 19:49:27
2楼:
感谢leozem,其实你表达的就是我的意思,只是类我是用构造函数Create,而不是new出来的。类能够用new吗?我搜索过网上说消息传递消息都是用record,record用new就可以,但类也可以么? 我跟leozem的区别是,leozem是new指针出来,使用后dispose,我的是create一个类,最后free掉,我先尝试一下new是否成功,感觉跟create是一个道理啊?? 我还是把我的重点代码贴一下出来直观一些,讨论也方便 主窗体向线程发送消息可以收到,但线程向主窗体发送消息时就报内存错误了。 主窗体登陆发送: procedure TfrmMain.btnLoginClick(Sender: TObject); var LD: TLoginData; begin LD := TLoginData.Create; 。。。 PostThreadMessage(FThread.ThreadID, WM_CUSTOM, Integer(@LD), 0); end; 线程接收登陆请求: procedure TCustomThread.Execute; var Msg: TMsg; LD: TLoginData; begin while not Terminated and GetMessage(Msg, 0, 0, 0) do begin if Msg.message = WM_CUSOM then begin LD := PLoginData(Msg.WParam)^; if Assigned(LD) then begin try 。。。 finally LD.Free; end; end; end; end; end; 反过来线程发送登陆结果: procedure TCustomThread.Login; var LDR: TLoginDataResult; begin LDR := TLoginDataResult.Create; 。。。 PostMessage(MainForm.Handle, WM_CUSTOM, Integer(@LDR), 0); end; 来窗体接收登陆结果 procedure ReceiveWorkThreadMessage(var Msg: TMessage); message WM_CUSTOM; procedure TfrmMain.ReceiveWorkThreadMessage(var Msg: TMessage); var LDR: TLoginDataResult; begin LDR := PLoginDataResult(Msg.WParam)^; if Assigned(LDR) then begin try 。。。 finally LDR.Free; end; end; end;
----------------------------------------------
-
作者:
leozem (leozem)
★☆☆☆☆
-
盒子活跃会员
2012/10/18 20:11:25
3楼:
我以为你TLoginData是一个Record,所以采用指针New的方式,如果你的TLoginData是一个class,就必须要用你的方法Create和free。New只是申请一段指针空间,而Create不仅仅申请还有一些类的初始化。 你提到的问题:主窗体向线程发送消息可以收到,但线程向主窗体发送消息时就报内存错误了。 初略的看了下代码,写法没什么问题。 你的MainForm是一个全局变量还是? 反过来线程发送登陆结果: procedure TCustomThread.Login; var LDR: TLoginDataResult; begin LDR := TLoginDataResult.Create; 。。。 PostMessage(MainForm.Handle, WM_CUSTOM, Integer(@LDR), 0); //调试下这里面的MainForm看看是不是空指针。 end;
----------------------------------------------
-
作者:
litou (泥头)
★☆☆☆☆
-
盒子活跃会员
2012/10/18 20:25:18
4楼:
to leozem,MainForm.Handle其实就是主窗口的句柄,我在Create这个线程的时候已经把Handle传进来了,我这样写是让大家看得明白这是主窗口的句柄。 出错的地方是这里: 来窗体接收登陆结果 procedure TfrmMain.ReceiveWorkThreadMessage(var Msg: TMessage); var LDR: TLoginDataResult; begin LDR := PLoginDataResult(Msg.WParam)^; if Assigned(LDR) then begin try LDR.xxx = xxx; //这里出错 finally LDR.Free; end; end; end; 经过调试和watch,线程发的那个Integer(@LDR)与接收的Msg.WParam值是一致的,排除值接收错误的原因,当接收处理到LDR := PLoginDataResult(Msg.WParam)^;这部时,调试显示LDR为inaccessible value,虽然程序能继续走下去,但到下面对LDR取值时就出现内存出错,可以肯定转换出来的LDR不是有效的LDR类型,但是WParam值又是对的,估计是WParam指向的对象被系统回收了,但是前面又没有free,所以觉得很奇怪。我曾经担心过这个指针值是因为没有被使用而被系统回收(类似Java、C#的回收机制)而使用了TList存放LD和LDR,但结果依然。我是使用FastMM的,退出时也没有报内存泄露,真奇怪。
----------------------------------------------
-
作者:
leozem (leozem)
★☆☆☆☆
-
盒子活跃会员
2012/10/18 20:46:16
5楼:
没释放不会被系统回收。 奇怪了,代码没问题。 1、32位系统? 2、线程是否在Dll中?
----------------------------------------------
-
作者:
litou (泥头)
★☆☆☆☆
-
盒子活跃会员
2012/10/18 20:48:43
6楼:
to leozem,32位xp,d7,纯win32程序无任何dll,我再继续调试排除一下,既然代码无错可能是一些小地方引起的,感谢leozem
----------------------------------------------
-
作者:
litou (泥头)
★☆☆☆☆
-
盒子活跃会员
2012/10/18 21:06:54
7楼:
从主窗体发送至线程,运行时能够得到转换成功的LD,但调试时得到的LD竟然是nil,再调试LD就编程inaccessible value了,难道真的不能够用类指针传递?因为网上搜到的都是用record,但record存放字符串会有问题吧?
----------------------------------------------
-
作者:
2012/10/18 21:19:30
8楼:
程序你显然写错了, 传递实例指针, 不是传局部变量指针, 所以你每次postthreadmessage 传递的指针都是错的, 根本不是实例的地址, 你应该这么写Integer(LDR) 而不是 Integer(@LDR), 另外接受到消息后, 直接转换回来即可不需要 PLoginDataResult, PLoginData这么2个东西, 按下面方式改 LDR := TLoginDataResult(Msg.WParam); LD := TLoginData(Msg.WParam);
----------------------------------------------
一只蜗牛
作者:
2012/10/18 21:21:28
9楼:
2处PostThreadMessage 和2处消息处理的地方都要改哦. 别漏了 :)
----------------------------------------------
一只蜗牛
作者:
litou (泥头)
★☆☆☆☆
-
盒子活跃会员
2012/10/18 21:42:22
10楼:
经测试,果然如hexpate所言,应该传实例指针,这是我受网上传record参数的方法影响,没有注意到类的实例与record在指针上的本质不同,再次感谢hexpate指出错误,也感谢leozem的帮助,该问题已完满解决。
----------------------------------------------
-