DLL中的窗体问题

木落 2008-12-12 07:37:29
我用CB做一个程序,主程序打开一个线程(用API,就是CreateThread),
然后线程里加载dll。一开始的写法如此:

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved)
{
FrmMain=new TFrmMain(NULL);
FrmMain->ShowModal();
}


但是发现窗体可以创建,但是不稳定。有时报错,有时新建的窗体忽然变到全屏,有时新建窗体大小不对等等。

据说在Delphi中需要修改Application对象等等,比如这段代码:

DllApplication:=Application;
DLLProc := @DLLUnloadProc;


但是在CB中,却深深地迷糊了。最后没办法,先放在一边。
由于程序较为特殊,需要在主程序的某个菜单中插入位于Dll中的某个菜单。
dll菜单中恰好需要一个“窗体隐藏/显示”功能,
于是去掉了FrmMain->ShowModal();,转而在Dll菜单中写到(已完成dll菜单与主程序的连接):

void __fastcall TFrmMain::N1Click(TObject *Sender)
{
Visible=!Visible;
N1->Checked=!N1->Checked;
}


然而运行时意外报错,
又将显示窗体的代码改为FrmMain->ShowModal();,然而又报错。。。

总结下来,有这么几个问题:
1.用API创建的线程是否能支撑一个VCL窗体
2.Application对象在Dll中是什么,起什么作用,还是主程序的Application在干活
3.是否有CB的Dll窗体的Demo,请大家为我提供一个
4.对于PopupMenu,这样的处理方式是否正确,还是必须将PopupMenu所在内存共享
5.窗体的Visible,Show函数,ShowModal函数的区别

PS:我的IDE是CB 2009,动态调用DLL
...全文
238 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
jxw1987628 2008-12-13
  • 打赏
  • 举报
回复

/*
不知道你是不是想实现在主程序中插入子菜单到dll中的某个窗体中呢,如果是这样,请参考下面的代码

*/

/***************************
结构体定义文件
*****************************/
typedef struct TModule{
TMainMenu* TrayMenu;
TForm* MainForm;
} TModule ;


/***********************************************

Project2.dll 源文件
**********************************************/
extern "C" {
void __declspec(dllexport) __stdcall InitializeForm(void);
void __declspec(dllexport) __stdcall UnitializeForm(void);
bool __declspec(dllexport) __stdcall GetInfo( TModule& moudle);
}
bool __stdcall GetInfo( TModule& moudle){
if(!FrmMain || !(FrmMain->TrayMenu) ) return false;
moudle.TrayMenu =FrmMain->TrayMenu ;
moudle.MainForm = FrmMain;
return true;
}

void __stdcall InitializeForm(void){
FrmMain = 0;
FrmMain = new TFrmMain(0);
if(!FrmMain){
throw Exception("create frmMain form failed!");
}
}

void __stdcall UnitializeForm(void){
if(FrmMain){
delete FrmMain;
FrmMain =0 ;
}
}


/*****************************************************************

exe源文件
*****************************************************************/

typedef void __stdcall (*ptrInitializeForm)(void);
typedef void __stdcall (*ptrUnitializeForm)(void);
typedef bool __stdcall(*ptrGetInfo)( TModule& moudle);
HINSTANCE hDLL;
ptrInitializeForm InitializeForm=0;
ptrUnitializeForm UnitializeForm=0;
ptrGetInfo GetInfo=0;
TModule module ;
module.TrayMenu =0 ;
module.MainForm =0 ;
hDLL=::LoadLibrary("Project2.dll");
if (hDLL){
try
{
try
{
InitializeForm=(ptrInitializeForm)::GetProcAddress(hDLL,"InitializeForm");
if(InitializeForm)
InitializeForm();
else
throw Exception("error at function InitializeForm!");
UnitializeForm=(ptrUnitializeForm)::GetProcAddress(hDLL,"UnitializeForm");
if(!UnitializeForm)
throw Exception("error at function UnitializeForm!");
GetInfo=( ptrGetInfo)::GetProcAddress(hDLL,"GetInfo");
if(GetInfo){
GetInfo(module);
TMenuItem *newItem =new TMenuItem(module.TrayMenu);
newItem->Caption="新增菜单";
module.TrayMenu->Items->Add(newItem);
newItem =new TMenuItem(module.TrayMenu);
newItem->Caption="新增菜单子菜单";
module.TrayMenu->Items->Items[0]->Add(newItem);//再在刚加入的菜单下面插入1个子菜单
module.MainForm->ShowModal(); //Show出来,看看是否插入菜单成功了
}
else
throw Exception("error at function GetInfo !");
}
catch(Exception& e){
ShowMessage(e.Message);
}
}
__finally
{
UnitializeForm(); //释放窗体内存
::FreeLibrary(hDLL);//释放库
}
}
木落 2008-12-13
  • 打赏
  • 举报
回复
用MDI搞定,结贴!
我来看看CB 2008-12-13
  • 打赏
  • 举报
回复
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved)
{
FrmMain=new TFrmMain(Application);
FrmMain->ShowModal();
}
看看行不
木落 2008-12-13
  • 打赏
  • 举报
回复
试试MDI程序~
经验告诉我们,对于YD的问题就要用更YD的方法解决
hnzlk 2008-12-13
  • 打赏
  • 举报
回复
JF
木落 2008-12-13
  • 打赏
  • 举报
回复
难道是VCL只允许单线程的魔咒???
那么我用多个Application对象可不可以呢?
木落 2008-12-13
  • 打赏
  • 举报
回复
“不知道你是不是想实现在主程序中插入子菜单到dll中的某个窗体中呢,如果是这样,请参考下面的代码”
与这个情况正好相反,是从Dll中提取一个菜单加入到主窗体的某个菜单中。。。

有点明白了,在某篇文章中找到这段话:


/*
在DLL中Application对象就不是真实的Application了
procedure TCustomForm.CreateParams(var Params: TCreateParams);
//....
WndParent := Application.Handle;
//....
所以Application.Handle的值就是0
当创建DLL窗体时,需要把当前运用程序的Application.Handle传递给DLL的Application.Handle
这样操作系统才不会把DLL中的窗体看成是另外一个运用程序的窗体
在释放的时候,需要将DLL中的Application.Handle复原(赋值为0)
这样当窗体关闭的时候系统才不会认为是当前运用程序关闭
*/


这样看来jxw1987628兄对于Dll中Application的解释是比较通俗的说法,
可以说对,也可以说不对。

用了一个TThread解决了几乎所有的问题:
class TModLoader : public TThread
{
private:
char* FPath;
protected:
void __fastcall Execute();
public:
__fastcall TModLoader(bool CreateSuspended,char* DllPath);
};


typedef TModule __stdcall (*pGetInfo)();
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
__fastcall TModLoader::TModLoader(bool CreateSuspended,char* DllPath)
: TThread(CreateSuspended)
{
FPath=DllPath;
}
//---------------------------------------------------------------------------
void Load(char* DllPath)
{
TModLoader *Thread;
Thread = new TModLoader(false,DllPath);
// 稍后把 Thread 的句柄放入链表
}
//---------------------------------------------------------------------------
void __fastcall TModLoader::Execute()
{
MessageBox(0,FPath,"",MB_OK);
HINSTANCE hDLL;
pGetInfo GetInfo;
hDLL=LoadLibrary(FPath);
if (hDLL!=0) {
GetInfo=(pGetInfo)GetProcAddress(hDLL,"GetInfo");
if (GetInfo!=NULL) {
TMenuItem *Item;
//Item=FrmMain->PopMenu->Items;
Item = GetInfo().TrayMenu;
FrmMain->P2->Add(Item);
//FrmMain->P2->Items->Insert(0,Item);
}
} else ExitThread(0);

while (!Terminated) {
if (WaitMessage()) {
Application->ProcessMessages();
}
}
}
//---------------------------------------------------------------------------

然而最初的,也是作为我使用线程的最初动机的问题来了:Dll窗体执行时,
主窗体被锁死。。。
大汗~~~~~~

写程序写得这么囧,我找块豆腐撞死算了。。。
木落 2008-12-12
  • 打赏
  • 举报
回复
另附DLL中的GetInfo过程:

TModule _export _stdcall GetInfo()
{
TModule Mod;
Mod.TrayMenu=FrmMain->TrayMenu->Items;
Mod.MainForm=FrmMain;
return Mod;
}
木落 2008-12-12
  • 打赏
  • 举报
回复
受教了!

1.那么关于线程,我该怎么做呢?
3.DLL入口:

#pragma argsused
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved)
{
FrmMain=new TFrmMain(NULL);
return 1;
}


4.给出自己的代码
定义:

typedef TModule __stdcall (*pGetInfo)();


typedef struct TModule{
TMenuItem *TrayMenu;
TForm *MainForm;
} TModule ;

DLL加载:

HINSTANCE hDLL;
pGetInfo GetInfo;
hDLL=LoadLibrary((char*)DLLPath);
if (hDLL!=0) {
GetInfo=(pGetInfo)GetProcAddress(hDLL,"GetInfo");
if (GetInfo!=NULL) {
TMenuItem *Item;
Item = GetInfo().TrayMenu;
FrmMain->P2->Add(Item);
// P2为一个TMenuItem
}
} else ExitThread(0);

jxw1987628 2008-12-12
  • 打赏
  • 举报
回复

1, 否

2. Application这个全局的TApplication对象指针是指当前加载dll的那个exe程序的TApplication对象指针

3. 没有demo,dll和exe差不多,就是new->dll wizard .然后往里面加入窗体,再接着code

4. 不清楚你是怎么操作PopMenu的

5. 记住这一点,你不能把一个Visible=true的窗体ShowModal,

通常你设置窗体的Visible=true实质上是在调用该窗体的Show()方法!ShowModal是把窗体以模态

方式显示出来,关于模态窗体的特性,你可以详细查看百度的介绍!

604

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder VCL组件使用和开发
社区管理员
  • VCL组件使用和开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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