MDI程序中EXE文件中和Application和DLL中的Application关于ActiveMDIChild内容不一致...

mimong_lin 2008-06-18 09:16:20
我的DLL中包含了fsMDIChild子窗体.使用如下的接口,一个用于获取子窗体的Form指针和classid,用于在DLL外部创建这个子窗体,这个时候已经是将DLL中的Application使用外面EXE的MDI Application进行替换,保证在EXE和DLL中的Application是一致的.另一个函数接口是用来观察外界传入的MDI Application和此刻DLL中的Application是否一致,观察结果是一致的.(见日志内容)

void GetFormClass(TApplication* pApp, struct FormClass& f)
{
if(g_SavedApp == NULL)
{
g_SavedApp = Application;

}
Application = pApp;

f.ppForm = (TForm **)&FormDLL;
f.formClass = __classid(TFormDLL);
}

void WatchApplication(TApplication** pOutApp, unsigned int *pAddr)
{
//传入的Application和内部的Application的地址,以后外面做比对
char addr[15];
AnsiString outAddr, inAddr;
memset(addr, 0, 15);
sprintf(addr, "%#010X[1]", (unsigned int)*pOutApp);
outAddr = AnsiString(addr);
memset(addr, 0, 15);
sprintf(addr, "%#010X[2]", (unsigned int)Application);
inAddr = AnsiString(addr);

Common::log.Message(Format("[DLL内部] 传入的[%s]Application->MainForm->ActiveMDIChild->Caption %s",
ARRAYOFCONST((outAddr, (*pOutApp)->MainForm->ActiveMDIChild->Caption))));
Common::log.Message(Format("[DLL内部] 内部的[%s]Application->MainForm->ActiveMDIChild->Caption %s",
ARRAYOFCONST((inAddr, Application->MainForm->ActiveMDIChild->Caption))));

AnsiString outFormAddr, inFormAddr;
memset(addr, 0, 15);
sprintf(addr, "%#010X[1]", (unsigned int)(*pOutApp)->MainForm->ActiveMDIChild);
outFormAddr = AnsiString(addr);
memset(addr, 0, 15);
sprintf(addr, "%#010X[2]", (unsigned int)Application->MainForm->ActiveMDIChild);
inFormAddr = AnsiString(addr);

Common::log.Message(Format("[DLL内部] 传入的[%s]Application->MainForm->ActiveMDIChild地址 %s",
ARRAYOFCONST((outAddr, outFormAddr))));
Common::log.Message(Format("[DLL内部] 内部的[%s]Application->MainForm->ActiveMDIChild地址 %s",
ARRAYOFCONST((inAddr, inFormAddr))));

AnsiString formAddr;
memset(addr, 0, 15);
sprintf(addr, "%#010X", (unsigned int)FormDLL);
formAddr = AnsiString(addr);
Common::log.Message(Format("[DLL内部] 内部Form的地址 [%s]", ARRAYOFCONST((formAddr))));

*pAddr = (unsigned int)(*pOutApp)->MainForm->ActiveMDIChild;
memset(addr, 0, 15);
sprintf(addr, "%#010X", (unsigned int)*pAddr);
formAddr = AnsiString(addr);
Common::log.Message(Format("[DLL内部] 保存传入的[%s]Application->MainForm->ActiveMDIChild地址 [%s]",
ARRAYOFCONST((outAddr, formAddr))));

*pOutApp = Application;
}


日志内容:
[2008-06-17 17:22:45] [DLL内部] 传入的[0X011A21AC[1]]Application->MainForm->ActiveMDIChild->Caption DLL窗体
[2008-06-17 17:22:45] [DLL内部] 内部的[0X011A21AC[2]]Application->MainForm->ActiveMDIChild->Caption DLL窗体
[2008-06-17 17:22:45] [DLL内部] 传入的[0X011A21AC[1]]Application->MainForm->ActiveMDIChild地址 0X011A7B60[1]
[2008-06-17 17:22:45] [DLL内部] 内部的[0X011A21AC[2]]Application->MainForm->ActiveMDIChild地址 0X011A7B60[2]
[2008-06-17 17:22:45] [DLL内部] 内部Form的地址 [0X011A7B60]
[2008-06-17 17:22:45] [DLL内部] 保存传入的[0X011A21AC[1]]Application->MainForm->ActiveMDIChild地址 [0X011A7B60]

但是在外部的EXE中记录的内容显示,ActiveMDIChild的值是空的

struct FormClass f;
TApplication* pDll = Application; //将Application传入DLL中,再将DLL中的Application带出
unsigned int addr;
GETFORMCLASSFUNC Get
= (GETFORMCLASSFUNC)::GetProcAddress(m_dllIns, "_GetFormClass");
if(Get == NULL)
{
throw(Exception(Format("函数地址获取失败:%u", ARRAYOFCONST(((int)::GetLastError())))));
}
Get(Application, f);

if(f.ppForm == NULL)
{
return ;
}

if((*f.ppForm) == NULL)
{
Application->CreateForm(f.formClass, f.ppForm);
WATCHAPPFUNC WatchApp
= (WATCHAPPFUNC)::GetProcAddress(m_dllIns, "_WatchApplication");
if(WatchApp == NULL)
{
throw(Exception(Format("函数地址取址失败:%u", ARRAYOFCONST(((int)::GetLastError())))));
}

WatchApp(&pDll, &addr);

char address[15];
AnsiString outAddr, inAddr, formAddr;
memset(address, 0, 15);
sprintf(address, "%#010X[1]", (unsigned int)pDll);
outAddr = AnsiString(address);
memset(address, 0, 15);
sprintf(address, "%#010X[2]", (unsigned int)Application);
inAddr = AnsiString(address);
memset(address, 0, 15);
sprintf(address, "%#010X", (unsigned int)addr);
formAddr = AnsiString(address);

Common::log.Message(Format("[EXE] 传出的[%s]Application->MainForm->ActiveMDIChild->Caption %s",
ARRAYOFCONST((outAddr, pDll->MainForm->ActiveMDIChild->Caption))));
Common::log.Message(Format("[EXE] EXE的[%s]Application->MainForm->ActiveMDIChild->Caption %s",
ARRAYOFCONST((inAddr, Application->MainForm->ActiveMDIChild->Caption))));
Common::log.Message(Format("取得地址 [%s],强制显示地址中的Caption %s",
ARRAYOFCONST((formAddr, ((TForm *)addr)->Caption))));
}
(*f.ppForm)->Show();


日志内容:
[2008-06-17 17:22:45] [EXE] 传出的[0X011A21AC[1]]Application->MainForm->ActiveMDIChild->Caption
[2008-06-17 17:22:45] [EXE] EXE的[0X011A21AC[2]]Application->MainForm->ActiveMDIChild->Caption
[2008-06-17 17:22:45] 取得地址 [0X011A7B60],强制显示地址中的Caption DLL窗体

调试查看ActiveMDIChild的值为NULL,但奇怪的是Application->MainForm->ActiveMDIChild->Caption这样的调用却不会报访问异常的错.

请教这是什么回事呢?有什么解决的方法吗?
...全文
167 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
mimong_lin 2008-06-26
  • 打赏
  • 举报
回复
我的DLL子窗体是在另一个管理DLL(这个DLL中没有任何的窗体)中维护的,外面的主程序通过调用管理DLL中的接口来显示这些DLL子窗体.这样的话,就接收不了消息了.

我现在的做法,在onclose消息中,delete this.但这存在一个问题,就是如果是在OnShow消息中调用了Close()的话,delete 就会出异常
sczyq 2008-06-25
  • 打赏
  • 举报
回复
另外,能保证 OnClose 中能够将参数 Action 设为 caFree

void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
Action = caFree;
}



如果关闭窗口后,还要释放DLL,你不能Close后,就FreeLibrary 这个DLL
正确的做法是:

1、在 MDIChild OnDestroy事件函数内最后给主窗口发送特定的自定义的消息。

2、在主窗口消息处理函数中接收到信息后,FreeLibrary 这个 DLL
sczyq 2008-06-25
  • 打赏
  • 举报
回复

一般一个简单的DLL MDIChild 窗口应该使用 new 来建立

//---------------------------------------------------------------------------

#include <vcl.h>
#include <windows.h>
#pragma hdrstop
#include "Unit2.h" // TUserForm2 表单

extern "C" __declspec(dllexport)
TForm * __stdcall UserForm2Form(TComponent* Owner);
//---------------------------------------------------------------------------
#pragma argsused

//---------------------------------------------------------------------------
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
//---------------------------------------------------------------------------
TForm * __stdcall UserForm2Form(TComponent* Owner) // 传入主窗口 Application
{
TUserForm2 *CallForm = new TUserForm2(Owner);
CallForm->Show();
return CallForm;
}
//---------------------------------------------------------------------------
mimong_lin 2008-06-24
  • 打赏
  • 举报
回复
终于都追原因了.
一方面是因为Application的Components中关闭已打开的报表子窗体在子窗体关闭后仍保留了子窗体的信息.
另一方面是,delete子窗体时,得在子窗体的onclose事件里调用delete this.在其它的地方调用delete来删除指针都是无效的.不知道是为什么.
mimong_lin 2008-06-24
  • 打赏
  • 举报
回复
跟踪了一天,发现,在DLL中Close MDIChild,delete 该指针,将指针置为空后,查看发现Application中的Components仍然还存在刚delete的东西,最后才造成了close 完子窗口后的访问违规.重申明一下,我是用Application的createform来创建窗体的,结果呢,ActiveMDIChild和MDIChildrens都报应不了刚创建的mdi child,而现在好了,关闭删除了mdi child后, Application的Components又死不放手了.

不知道是我还做少了什么操作?
sczyq 2008-06-23
  • 打赏
  • 举报
回复
只能跟踪程序,在哪里出错
mimong_lin 2008-06-23
  • 打赏
  • 举报
回复
那个示例下载不了.

自己管理子窗口的话,自己倒是做了些许,达到功能上的要求了.

但现在还有一个问题,就是在关闭DLL中的子窗体时,报内存访问出错,调试跟踪的时候,都已经是从子窗体的Close事件中出来了,又回到了主程序中.

不知道楼上的以前有没有遇到这样的问题呢?
sczyq 2008-06-19
  • 打赏
  • 举报
回复
若说 DLL 中的 MDI 问题多多, 那是他们不知如何解决, 解决了就没问题.

我们在制做调用 DLL 中 MDI FORM 的主程序中, 对于 MDIForm 的管理并不依赖于 ActiveMDIChild 和 MDIChildCount, 而是自己定义类来进行管理.

有关信息可参见 BLOG

http://blog.csdn.net/sczyq/archive/2007/03/20/1534977.aspx
mimong_lin 2008-06-19
  • 打赏
  • 举报
回复
晕倒,怎么没人回复呢?
mimong_lin 2008-06-18
  • 打赏
  • 举报
回复
顶上去啊.

昨天在网上搜的时候,有个03年的CSDN贴,说是MDI的问题多多,不知道现在的这些问题有没有补啊.

我用的是CB6 + SP4.

该问题的简洁描述是,创建DLL中的MDI子窗体时,MDI的MainForm的ActiveMDIChild和MDIChildCount这些有关MDI子窗体的属性都没有表现出新创建了一个MDI子窗体.

13,826

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder相关内容讨论区
社区管理员
  • 基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧