有关dllmain中进程退出时的问题

sunnycrystal_2008 2008-07-09 06:49:53

我想让一个.exe的应用程序在结束之前调用dllmain中DLL_PROCESS_DETACH分支中的一个线程函数,如下:

DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
hThreadHandle = (HANDLE)_beginthreadex(NULL, 0, &ThreadFun,
NULL, 0, &threadid);
if(WaitForSingleObject(hThreadHandle,INFINITE)==WAIT_O_OBJECT) //运行到此处就死住了,根本到不了线程函数
CloseHandle(hThreadHandle);
}
unsigned _stdcall ThreadFun(void *param)
{
printf("MyNewThread Is Begin");
return 0;
}
哪位高手能帮我解决一下,谢谢!
...全文
429 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
小熊妞 2012-03-09
  • 打赏
  • 举报
回复
不要在DllMain中使用WaitForSingleObject,我的想法是设置一个全局变量g_bStopThread,等待线程结束

do
{
Sleep(SLEEP_INTERVAL);

} while (FALSE == g_bStopThread);

g_bExitThread = TRUE;
CloseHandle(hThread);
hThread = NULL;
sunnycrystal_2008 2008-07-14
  • 打赏
  • 举报
回复
对,楼上说得很有理,我今天试了,DisableThreadLibraryCalls真的不行,谢谢大家了,我准备结贴了,但是分数有限,大家不要嫌太少哦。
nevergone 2008-07-14
  • 打赏
  • 举报
回复
from 《windows 核心编程》:
通过进一步的研究,我终于发现了问题。当进程被创建时,系统也创建一个互斥对象。每个进程都有它自己的互斥对象,也就是说多个进程并不共享互斥对象。当线程调用映射到进程的地址空间中的D L L的D l l M a i n函数时,这个互斥对象负责对进程的所有线程实施同步。
当C r e a t e T h r e a d函数被调用时,系统首先创建线程的内核对象和线程的堆栈。然后它在内部调用Wa i t F o r S i n g l e O b j e c t函数,传递进程的互斥对象的句柄。一旦新线程拥有该互斥对象,系统就让新线程用D L L _ T H R E A D _ AT TA C H的值调用每个D L L的D l l M a i n函数。只有在这个时候,系统才调用R e l e a s e M u t e x,释放对进程的互斥对象的所有权。由于系统采用这种方式来运行,因此添加对D i s a b l e T h r e a d L i b r a r y C a l l s的调用,并不会防止线程被暂停运行。防止线程被暂停运行的唯一办法是重新设计这部分源代码,使得Wa i t F o r S i n g l e O b j e c t不会在任何D L L的D l l M a i n函数中被调用。

对于DLL_PROCESS_DETACH同样适用.
所以不要在DLL_PROCESS_DETACH中等待线程

重新设计你的线程吧
csx007700 2008-07-13
  • 打赏
  • 举报
回复
14 楼的方法好像不行啊
创建进程的时候,系统建立了一个互斥对象.每个进程都有自己的互斥对象,映射到DllMain时,互斥对象会合一个进程所有线程同步。
调用CreateThread时,系统首次创建线程内核对象。然后你调用
WaitForSingleObject传递互斥句柄,有了互斥对象系统又会调用每个Dll的DLL_THREAD_ATTACH,这时系统才会调用ReleaseMutex撤销互斥对象所有权。所以DisableThreadLibraryCalls()不能阻止死锁。你只能想办法不要调用WaitForSingleObject一系列函数才行。

sunnycrystal_2008 2008-07-13
  • 打赏
  • 举报
回复
to abomber2:
  加上你添加的函数就可以吗?明天我试试。
sunnycrystal_2008 2008-07-10
  • 打赏
  • 举报
回复
是写错了,应该是在DLL_PROCESS_DETACH下调用,不好意思。
我的实例只是随便写了一个,主要目的是要问在DLL_PROCESS_DETACH下能不能调用线程函数。
abomber2 2008-07-10
  • 打赏
  • 举报
回复
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)

DisableThreadLibraryCalls(hInstance);
hThreadHandle = (HANDLE)_beginthreadex(NULL, 0, &ThreadFun,
NULL, 0, &threadid);
if(WaitForSingleObject(hThreadHandle,INFINITE)==WAIT_O_OBJECT) //运行到此处就死住了,根本到不了线程函数
CloseHandle(hThreadHandle);
}
unsigned _stdcall ThreadFun(void *param)
{
printf("MyNewThread Is Begin");
return 0;
}
huangyuelei01 2008-07-09
  • 打赏
  • 举报
回复
我从来也不知道WaitForSingleObject有WAIT_O_OBJECT这个返回值。
dbger 2008-07-09
  • 打赏
  • 举报
回复
建议看微软关于DLL的白皮书:
http://www.microsoft.com/whdc/driver/kernel/DLL_bestprac.mspx
IsItNothing 2008-07-09
  • 打赏
  • 举报
回复
你这样作是不合适的,应该再DLL_PROCESS_DETACH下调用
if(WaitForSingleObject(hThreadHandle,INFINITE)==WAIT_O_OBJECT) //运行到此处就死住了,根本到不了线程函数
CloseHandle(hThreadHandle);
}
另外你的实例程序我看了费解,不知道你要干什么.
IsItNothing 2008-07-09
  • 打赏
  • 举报
回复
你这样作是不合适的,应该再DLL_PROCESS_DETACH下调用
if(WaitForSingleObject(hThreadHandle,INFINITE)==WAIT_O_OBJECT) //运行到此处就死住了,根本到不了线程函数
CloseHandle(hThreadHandle);
}
另外你的实例程序我看了费解,不知道你要干什么.
sunnycrystal_2008 2008-07-09
  • 打赏
  • 举报
回复
不好意思,是写错了,应该是:
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_DETACH)
hThreadHandle = (HANDLE)_beginthreadex(NULL, 0, &ThreadFun,
NULL, 0, &threadid);
if(WaitForSingleObject(hThreadHandle,INFINITE)==WAIT_O_OBJECT) //运行到此处就死住了,根本到不了线程函数
CloseHandle(hThreadHandle);
}
unsigned _stdcall ThreadFun(void *param)
{
printf("MyNewThread Is Begin");
return 0;
}

该线程函数不是死循环函数,如果不在dllmain()中调用,在其他地方调用是可行的。

请问一下dbger:
怎么用导出函数呢,能说得具体一点吗?
sunnycrystal_2008 2008-07-09
  • 打赏
  • 举报
回复
不好意思是写错了,应该是:
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_DETACH)
hThreadHandle = (HANDLE)_beginthreadex(NULL, 0, &ThreadFun,
NULL, 0, &threadid);
if(WaitForSingleObject(hThreadHandle,INFINITE)==WAIT_O_OBJECT) //运行到此处就死住了,根本到不了线程函数
CloseHandle(hThreadHandle);
}
unsigned _stdcall ThreadFun(void *param)
{
printf("MyNewThread Is Begin");
return 0;
}
该线程函数不是死循环函数,在dllmain()以外的地方调用都是可以执行的。

还想问一下dbger怎么用导出函数呢,能说得具体一点吗?
sunnycrystal_2008 2008-07-09
  • 打赏
  • 举报
回复
不好意思,是写错了,应该是:
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if (dwReason == DLL_PROCESS_DETACH)
hThreadHandle = (HANDLE)_beginthreadex(NULL, 0, &ThreadFun,
NULL, 0, &threadid);
if(WaitForSingleObject(hThreadHandle,INFINITE)==WAIT_O_OBJECT) //运行到此处就死住了,根本到不了线程函数
CloseHandle(hThreadHandle);
}
unsigned _stdcall ThreadFun(void *param)
{
printf("MyNewThread Is Begin");
return 0;
}

该线程函数不是死循环函数,如果不在dllmain()中调用,在其他地方调用是可行的。

请问一下dbger:
怎么用导出函数呢,能说得具体一点吗?
jwybobo2007 2008-07-09
  • 打赏
  • 举报
回复
DLL_PROCESS_DETACH通知中的函数怎么会在DLL_PROCESS_ATTACH里面执行,楼主写错了吧.
而且请问,该线程函数是死循环线程么?如果是,那么WaitForSingleObject(hThreadHandle,INFINITE)肯定会永远等待下去
dbger 2008-07-09
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 sunnycrystal_2008 的回复:]
那如果我需要在dllmain()中调用线程函数怎么办呢,有什么好办法解决呢?
[/Quote]

建议用导出函数来处理。
sunnycrystal_2008 2008-07-09
  • 打赏
  • 举报
回复
那如果我需要在dllmain()中调用线程函数怎么办呢,有什么好办法解决呢?
stivenjia 2008-07-09
  • 打赏
  • 举报
回复
你在ThreadFun中插入一句MessageBox(NULL,"Test","Test",MB_OK);
你看看你的那个线程到底执行没?
第二WaitForSingleObject(线程句柄)我从来没这么用过并且ms也没有承诺过他的这个WaitForSingleObject的句柄可是个线程句柄
第三进程加载你这个dll时候会将其映射到进程虚拟执行空间,并执行dllmain(如果有)
dbger 2008-07-09
  • 打赏
  • 举报
回复
不要在DllMain中做启动线程的操作,因为会导致死锁。
第一部分 概 述 第1章 调试工具简介 1 1.1.泄漏诊断工具 1 1.2.Windows调试工具集 3 1.3.UMDH 4 1.4.Microsoft.应用程序验证器 4 1.5.全局标志 9 1.6.进程浏览器11 1.7.Windows驱动程序开发包 12 1.8.Wireshark 14 1.9.DebugDiag 15 1.10.小结 15 第2章.调试器简介 16 2.1.调试器的基础知识 16 2.1.1.调试器类型 17 2.1.2.调试器命令 18 2.1.3.调试器的配置19 2.1.4.通过内核态调试器重定向用户 态调试器 24 2.1.5.是否使用KD 26 2.2.基本的调试任务26 2.2.1.键入调试命令27 2.2.2.解析调试器的提示信息27 2.2.3.配置和使用符号 29 2.2.4.使用源文件 38 2.2.5.分析命令40 2.2.6.修改上下文的命令60 2.2.7.其他的辅助命令 67 2.2.8.示例68 2.3.远程调试 70 2.3.1.Remote.exe 70 2.3.2.调试服务器 71 2.3.3.进程服务器与内核服务器 73 2.3.4.远程调试的符号解析74 2.3.5.远程调试的源代码解析 75 2.4.调试场景 75 2.4.1.调试非交互式进程(服务 或者COM服务器) 76 2.4.2.在没有内核态调试器的情况 下调试非交互式进程(服务 或者COM服务器) 77 2.5.小结 77 第3章.调试器揭密 78 3.1.用户态调试器的内幕78 3.1.1.操作系统对用户态调试器的支持78 3.1.2.调试事件的顺序 83 3.1.3.控制来自调试器的异常和事件 84 3.1.4.内核态调试器的调试事件处理105 3.2.控制调试目标 106 3.2.1.断点的工作原理 107 3.2.2.内存访问断点的工作原理 108 3.2.3.处理器跟踪 109 3.2.4.实调试的线程状态管理109 3.2.5.通过用户态调试器来挂起线程 112 3.3.小结 113 第4章.符号文件与源文件的管理 114 4.1.调试符号的管理114 4.1.1.公有符号的生成 115 4.1.2.在符号库存储符号 117 4.1.3.在HTTP服务器上共享公有符号 119 4.2.源文件的管理 120 4.2.1.收集源文件信息 120 4.2.2.源文件信息的使用122 4.2.3.不带源文件修订控制的源文件 服务器 123 4.3.小结 125 第二部分.调 试 实 践 第5章.内存破坏之一—栈 127 5.1.内存破坏的检测过程128 5.1.1.步骤1:状态分析 128 5.1.2.步骤2:源代码分析 129 5.1.3.步骤3:使用内存破坏检测工具 133 5.1.4.步骤4:调整源代码 133 5.1.5.步骤5:定义回避策略 133 5.2.栈内存破坏133 5.2.1.栈溢出 142 5.2.2.异步操作与栈顶指针 147 5.2.3.调用约定的不匹配154 5.2.4.回避策略164 5.3.小结 166 第6章.内存破坏之二—堆 167 6.1.堆简介167 6.1.1.前端分配器 168 6.1.2.后端分配器 169 6.2.堆破坏181 6.2.1.使用未初始化状态181 6.2.2.堆的上溢与下溢 185 6.2.3.堆句柄的不匹配 195 6.2.4.重用已删除的堆块199 6.3.小结 205 第7章.安全 206 7.1.Windows安全概述 206 7.1.1.安全标识符 207 7.1.2.访问控制列表208 7.1.3.安全描述符 209 7.1.4.访问令牌211 7.2.安全信息的来源213 7.2.1.访问令牌213 7.2.2.安全描述符 215 7.3.如何执行安全检查 217 7.4.在客户端/服务器程序传播标识 218 7.4.1.远程认证与安全支持提供者接口218 7.4.2.模拟级别220 7.5.系统边界上的安全检查 220 7.6.安全故障的分析221 7.6.1.本地安全故障221 7.6.2.延迟初始化的安全问题 226 7.6.3.身份模拟的潜在安全问题 231 7.6.4.分布式COM错误 232 7.6.5.扩展命令!token的故障241 7.6.6.在Windows XP SP2上安装了 某个程序后发生DCOM激活故障243 7.6.7.通过跟踪工具来分析安全故障 247 7.7.小结 248 第8章.进程间通信 249 8.1.通信机制 249 8.2.本地通信分析 250 8.2.1.LPC的背景知识 251 8.2.2.调试LPC通信251 8.2.3.调试本地DCOM以及MSRPC通信 254 8.3.远程通信分析 260 8.3.1.RPC故障测定状态信息的使用 260 8.3.2.网络流量分析270 8.3.3.打破调用路径275 8.4.一些其他的技术信息277 8.4.1.远程认证277 8.4.2.RPC扩展错误信息 278 8.4.3.其他工具278 8.5.小结 279 第9章.资源泄漏 280 9.1.什么是资源泄漏280 9.2.高层流程 280 9.2.1.步骤1:找出潜在的资源泄漏 281 9.2.2.步骤2:什么东西正在泄漏 282 9.2.3.步骤3:初步分析 282 9.2.4.步骤4:资源泄漏检测工具 282 9.2.5.步骤5:制定回避策略 283 9.3.资源泄漏的可重现性283 9.4.句柄泄漏 284 9.4.1.存在泄漏的程序 285 9.4.2.步骤1和步骤2:它是不是一个 句柄泄漏 286 9.4.3.步骤3:初始分析 287 9.4.4.更复杂的程序290 9.4.5.步骤4:利用泄漏检测工具 292 9.4.6.句柄注入与!htrace 298 9.4.7.步骤5:为句柄泄漏制定回避策略 300 9.5.内存泄漏 301 9.5.1.一个简单的内存泄漏 301 9.5.2.步骤1和步骤2:是否存在泄漏, 以及泄漏的是什么资源 302 9.5.3.步骤3:使用内存检测工具 303 9.5.4.步骤4:回避策略 322 9.6.小结 322 第10章.同步 323 10.1.同步的基础知识 323 10.1.1.事件 323 10.1.2.临界区 325 10.1.3.互斥体 329 10.1.4.信号量 330 10.2.高层流程 331 10.2.1.步骤1:识别问题的征兆 331 10.2.2.步骤2:转储所有线程331 10.2.3.步骤3:分析线程的同步问题332 10.2.4.步骤4:修复问题334 10.2.5.步骤5:制定回避策略334 10.3.同步情况 334 10.3.1.死锁 334 10.3.2.第1种孤立临界区情况—异常 338 10.3.3.第2种孤立临界区情况—线程 结束 343 10.3.4.DllMain函数的注意事项 347 10.3.5.锁竞争 353 10.3.6.管理临界区 358 10.4.小结 361 第三部分.高 级 主 题 第11章.编写定制的调试扩展363 11.1.调试扩展简介 363 11.2.调试扩展示例 365 11.2.1.调试扩展模型 369 11.2.2.调试扩展示例的需求 371 11.2.3.头文件和代码组织 372 11.2.4.调试扩展的初始化 374 11.2.5.调试会话状态的变化 379 11.2.6.KnownStructOutput 379 11.2.7.退出调试扩展 379 11.2.8.Help命令的实现 380 11.2.9.dumptree命令的实现 381 11.2.10.KnownStructOutput函数的实现 384 11.2.11.取消命令的实现387 11.2.12.版本 389 11.2.13.调试扩展的构建389 11.3.小结 390 第12章.64位调试 391 12.1.Microsoft 64位系统 391 12.1.1.操作系统简介 392 12.1.2.在WOW64运行的32位程序 393 12.2.Windows x64带来的变化395 12.2.1.第1章—调试工具简介 396 12.2.2.第2章—调试器简介 397 12.2.3.第3章—调试器揭密 407 12.2.4.第5章—内存破坏之一—栈 411 12.2.5.第6章—内存破坏之二—堆 411 12.2.6.第7章—安全 412 12.2.7.第8章—进程间通信 413 12.2.8.第11章—编写定制的调试扩展414 12.3.小结 414 第13章.事后调试 415 13.1.转储文件基础 415 13.1.1.通过调试器来生成转储文件 417 13.1.2.通过ADPlus来生成转储文件 420 13.1.3.内核态转储文件的创建 421 13.2.转储文件的使用 423 13.2.1.转储文件的分析:访问违例 424 13.2.2.转储文件的分析:句柄泄漏 425 13.3.Windows错误报告 429 13.3.1.Dr.Watson 429 13.3.2.Windows错误报告的系统架构 434 13.4.企业错误报告 446 13.4.1.设置企业错误报告 447 13.4.2.通过企业错误报告来报告错误 449 13.5.小结 451 第14章.功能强大的工具452 14.1.调试诊断工具 452 14.1.1.分析内存泄漏或者句柄泄漏 453 14.1.2.编写定制的分析脚本 455 14.2.扩展命令!analyze 457 14.2.1.故障程序 457 14.2.2.分析结果 458 14.2.3.故障的跟进人员 462 14.3.小结 463 第15章.Windows Vista基础 464 15.1.第1章—调试工具简介 464 15.2.第2章—调试器简介 465 15.2.1.用户访问控制的副作用 465 15.2.2.启用内核态调试器 467 15.2.3.地址空间布局的随机化 468 15.3.第6章—内存破坏之二—堆 469 15.4.第7章—安全性 473 15.4.1.用户访问控制 474 15.4.2.调试器的UAC 475 15.4.3.注册表和文件虚拟化 479 15.5.第8章—进程间通信 481 15.6.第9章—资源泄漏 482 15.7.第10章—同步482 15.7.1.轻量读写锁 482 15.7.2.条件变量 483 15.7.3.单次初始化 484 15.7.4.增强线程池 484 15.8.第11章—编写定制的调试扩展 484 15.9.第13章—事后调试485 15.10.小结487 附录A.应用程序验证器的测试设置488

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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