大富翁的高手呢?是高手的,请进来看看这个问题...分不够,再加! (200分)

  • 主题发起人 主题发起人 ddev
  • 开始时间 开始时间
D

ddev

Unregistered / Unconfirmed
GUEST, unregistred user!
MDI 应用,一个子窗体,是作为容器用的,加载的内容同样是一个窗体,
不过是在 DLL 中。基本过程如下:
1、子窗体调用入口(D5E):
function ShowDllForm(hParentWnd: HWND, ...): HWND;
stdcall;
{
DllForm = TDllForm.CreateParented(hParentWnd);
Result := DllForm.Handle;
...
}
2、主调用(BCB5E)
在子窗体 OnShow 及 OnResize 中同时处理,
其中 hDllWnd 为调用窗体(DLL窗体)的句柄:
HWND __fastcall XXXMDI::ShowDllForm(void)
{
HMODULE hDllModule = theApp->hDllModule;
HWND hWnd = NULL;
if (FAttachProject != NULL &&
hDllModule != NULL)
{
typedef HWND (DLL_PROC)(HWND, const char*, int);
DLL_PROC* lpDllProc = (DLL_PROC*)GetProcAddress(hDllModule, "ShowDllForm");
if (lpDllProc != NULL)
hWnd = (*lpDllProc)(Handle, AFileName.c_str(), dwFlag);
}
}
return hWnd;
}
void __fastcall XXXMDI::OnFormResize(...)
{
//如果 DLL 窗体没有加载,则加载
if (hDllWnd == NULL)
hDllWnd = ShowDllForm();
//调整窗体大小
if (hDllWnd != NULL)
SetWindowPos(hDllWnd, HWND_TOP,
0, 0, ClientWidth, ClientHeight,
SWP_SHOWWINDOW);
}
问题说明:在加载及程序本身运行过程全部正常,在退出程序时,
如果先把 XXXMDI 子窗体关闭,然后再关闭主窗体,也正常,但
如果先不关闭该子窗体而直接关闭程序,则出错,看 CPU 调试,
出现调试地址的内容全部为 ??? 。要说明的是,出错肯定是在该
载有 DLL 的窗体上,其他过程全部正常。
急死了,请教各位大侠帮帮我分析一下错误原因吧...
 
补充一点:
在关闭 XXXMDI 子窗体时,先向 DLL 窗体发送 WM——CLOSE
消息。保证 DLL窗体在 XXXMDI 窗体关闭前关闭。
 
现在调试点发现:
在 XXXMDI 子窗体关闭过程中,单步进行,一直能够进行到函数结束,
但随后就显示出错!用 try{}__finally{}也无法屏蔽,可以说已经是
系统内存读写错。为什么会这样呢???整个儿晚上都浪费了.......
 
将 DLL Form Show 中的 CreateParented 改成 SetParent 试试。
如果一定使用 CreateParented ,则在关闭 DLL Form 时,是不是需要释放什么。
 
to 小雨哥:
注意我前面的调试过程:如果先关闭 MDI 子窗体,然后再关闭主窗体,
一切过程全部正常;问题就是出在直接关闭包含了该子窗体的情况下的
主窗体,一动就出错。根据调试应该是某片内存在这时已经无效,而
MDIMain 却仍旧试图去引用这片内存(CPU 调试断点前后全是 ???)。
有谁比较清楚 MDI 应用中的窗口消毁过程的,请给出一些指点吧——
 
还有一点:在 DEBUG 状态下,MDI 窗体的 OnResize 事件处理
的 DLL 窗口大小调整有效(即能正常最大化);而在 RELEASE
状态下,却发现 DLL 窗口总是不断被重新创建(很明显,所有
的状态全部被回复!真奇怪),而且根本不响应 SetWindowPos
函数的调整!
死惨了 ------ 本来还准备一天能够完工的,现在不知要拖多久了.....
歉的是对客户的承诺泡汤,恨的是 BORLAND 的组件特性(RTTI)实在是
问题多多,怨的是微软明知 MDI 应用有着不可告人的缺陷,却再
也不去修正,而且在它的几个主要产品中还频频使用,拐诱良家少女啊···
 
继续调试发现:
如果强制发出 WM_DESTROY,关闭每个窗体,则程序可以正常退出(调试状态),
即关闭子窗体过程中,先发 WM_CLOSE, 继续再发 WM_DESTROY。程序就完全正常了。
但 ---------- 更大的问题在后面(Release 状态):
1、如果 stack frames 关闭(未核选),则出现每次 MDI 窗体大小调整时,都会
重新创建 DLL 窗体!而且窗体位置根本就不动(挂在右下脚,就是不能正常最大化);
2、如果打开 stack frames 选项,窗体最大最小化正常,但这时出现:
priviledge instruction 错误信息,叫什么?我还是第一次遇到,强制权限?
最后关闭程序时,出现无效内存调用!同以前一样!不知道为什么?
==========================================================================
难道没有人能解决这个问题吗?!大富翁的高手何在?
 
前面的关闭 stack frames 是关闭 C 编译选项中的,
如果关闭 PASCAL 中的,则出现 external exception XXXXXXXX,
外部异常!到这儿,可以肯定是 RTTI 出现问题,估计 BCB 在处理
DLL 中的 VMT 和应用中的 VMT 时,用了什么“VCL”的手法,
导致数据交换出现严重异常!VCL 在宿主问题上,到底有什么特殊
的方法(但显然是不安全的,或者说是不干净的!)呢?
 
//FORM1 : 主窗体
procedure TForm1.FormCloseQuery(Sender: TObject;
var CanClose: Boolean);
var
I : Integer;
begin
if Self.MDIChildCount > 0 then
begin
For I := Self.MDIChildCount - 1do
wnto 0do
begin
try
Self.MDIChildren.Close;
except
end;
end;
end;
end;
 
MDI应用程序关闭的机制是这样的:如果直接关主窗体,会首先触发各个子窗体的
OnCloseQuery事件,如果在某一个子窗体的OnCloseQuery事件中不允许关闭子
窗体,则返回主程序。如果每个子窗体的OnCloseQuery事件都允许关闭,则触发
主窗体的OnCloseQuery事件,然后触发主窗体的Close事件,程序结束(并不触发
子窗体的Close事件)。程序退出时没被释放的窗体会由Screen全局变量负责释放
所有窗体,你的程序出错是否是因为Dll释放时没有手动释放
建议在程序结束前,Dll释放前(FreeLibrary)手动释放掉Dll中创建的窗体(必须
在Dll中释放)
 
已经很久了,今天再次看这个帖子,仔细体味 ddev 自己的描述,觉得不是 BCB 或 Delphi
的问题。这个现象看起来是 DLL 设计有错误。
DllForm = TDllForm.CreateParented(hParentWnd) 这句,关联到了 XXXMDI->Handle ,而
且是 Parent 而不是 Owner 。Parent 和 Owner 是有本质的区别的。
由于是 Parent 被关联到了 XXXMDI ,正常情况下,它自己的 Handle 不再外显,对其
Handle 的操作要注意窗体层次才会有反映(肯定不是 HWND_TOP)。
至于关闭堆检查的问题,一般 DLL 将被映射到 EXE 的地址空间,这时 DLL 仍然有自己独
立的堆,很多时候我们要注意在调用 DLL 中的对象时,一定要检查这个对象是否仍然在 DLL
自己的堆内被创建和管理,不然后果是不能预料的。我们打开堆检查时,编译器为我们提交
堆分配,直到彻底失败为止。
所以,我认为是 DLL 设计的问题。改变调用方法,由 DLL 的 Application 自己管理,或
把 DLL 的释放权交给 XXXMDI ,或其他的什么,应该就正常了。
 
to 小雨哥:
隔了这么长的时间,没想到你还能够来,真是太感动了(真的)。
现在,我对这个问题也是有一个解法了,其实这也应该归于 APP
与 DLL 之间的共享问题。因为在许多问题上,Delphi/BCB 隐藏
了一些细节,并且其内部可能处理,也可能不处理,因为在做 DLL
的时候,如果出现 VCL 窗体,不管它的功用如何,总是应该传递一
个 Application->Handle,即 hInst = Application->Handle
给 DLL 的接口函数,然后在接口函数中总是加一条声明:
Application->Handle = hInst;
然后再作其他的处理,可以避免许多的麻烦和出错,APP 和 DLL 的
内存映射并不是一致,通过这条代码,可以使得它们能够同步或一致。
而且,进一步发现:Delphi 在这方面的处理要比 BCB 高级一点,一般
情况下,它能够相对“智能”地处理,最多是显示一个额外的进程窗体。
-------------------------------------------------------------------------
谢谢各位的参与,该结束了...
 
后退
顶部