注册 登录
编程论坛 C++教室

把函数调用改为直接执行其代码,为何导致不能调试?

tm1mc2 发布于 2014-09-28 11:03, 945 次点击
我把别人的一个能够正常调试的Dll程序拿来修改。只修改一项:取消一个函数的调用。不调用了,直接执行其代码。
这个函数叫做RegisterIMEClass,它的功能是注册四个窗口类并建立4个窗口。
由于整个程序(由多文件组成)只有一个地方调用此函数,因此我觉得没有必要采用调用函数的方式,就把函数里的程序段直接代替原来的调用函数语句。
调用此函数的语句在Dll程序的入口函数DllMain的开头,见下图中划红线的语句:
只有本站会员才能查看附件,请 登录

被调用的函数参见下图:
只有本站会员才能查看附件,请 登录

取消调用方式了,就把该程序段中的形参hInstance都用实参g_hInst取代。
此外也取消函数申明。
原函数中的第一句(划红线的那句)“WNDCLASSEX wc;”定义了一个结构变量。
按规定,C语言的变量定义要放在函数开头,或者放到所有函数的前面作为全局变量。因此我把这条语句上移。
结果程序变成这样:
只有本站会员才能查看附件,请 登录

这个程序是个汉字输入方法,是个Dll,需要有个调用它的应用程序,一般选择记事本程序NOTEPAD.EXE来调试输入法Dll。
调试那个别人的程序时很正常:按F5以后记事本窗口跳出,选择此输入法后记事本窗口隐退,VC++回到前台,程序就从DllMain进入,到断点停下。
可是我这个改动后的程序,选择输入法后记事本仍然占据前台不肯隐退。这时按字母键的话,字母就出现在记事本上。这样就就无法调试。

想调试的朋友,到此下载程序:
http://www.
下载其中的“初中阶段示范程序”、拼音码表
如果你的系统是XP,就把下载的词库文件py.txt拷贝到:\WINDOWS\system32
建一个文件夹,把初中阶段示范程序《输入法编程入门标准程序.rar 》拷进去双击它,会释放出很多文件和文件夹。把其中的文件夹“说明”中的imm.h覆盖你的VC++的旧版本。
然后进入:Build->set Active Configuration,选中:srf-Win32 Release 。
然后选noteped.exe来调试本Dll。
做法是:
    Project->Settings->Debug->Executable for debug session
单击它下面右边的按钮,点击Browse...。对于windowsXP系统,它在C:\WINDOWS\NOTEPAD.EXE
设置好断点后,点击F5,VC就会打开记事本,在Windows的状态栏中选择“天下无敌”输入法,就可以像调试普通可执行程序一样调试这个输入法了。

[ 本帖最后由 tm1mc2 于 2014-9-28 11:20 编辑 ]
11 回复
#2
天使梦魔2014-09-28 22:05
不太想下,结果给出的代码实在没什么,一看链接居然注册。

这样说吧
WNDCLAS是用于声明窗口方式,你自己填信息,其中.hInstance成员是窗口编号,这是最重要的
设置完成后RegisterClass函数用于注册,等于调整信息。
最后CreateWindow调用这个已经注册的编号,它会返回一个句柄。(每个窗口独立句柄)
再然后ShowWindow调用这个句柄显示窗口,你需要UpdateWindow函数刷新这个句柄。
这是整个创建窗口流程,这些函数有EX版本的。


你检查下哪里有问题吧
#3
tm1mc22014-09-29 17:38
现在把问题简化:只在路路通的程序中加一句废话(图中红线所示),结果也能造成不能调试。然后把这句废话删除,又能正常调试。
是不是因为我在加入一句后漏了该做的一点事情?
我在加进这句话之后做的事是:存盘、编译、链接、全部链接、按F5。我还需要做啥?
只有本站会员才能查看附件,请 登录
#4
tm1mc22014-09-29 18:01
把这句话改成g_hInst=hInstDLL;也不行。
把这句话改成hInstDLL=hInstDLL;也不行。
改成只有一个分号的空语句(连同后面的注释)倒是可以的。
#5
天使梦魔2014-09-29 20:18
说了看下核心进程的书了,顺序顺序<windows核心编程>
dll顺序,你的4个窗口是独立线程吗?
DLL_THREAD_ATTACH是自身创建的时候不运行,而往后任何一个新线程创建就会直接跳到这里,而忽略掉上面
switch的运行顺序知道吧,假如是新线程状态dllmain返回DLL_THREAD_ATTACH从而直接跳过上面.如果你的窗口是独立线程的话那么悲剧了.
<windows核心编程>里同样提到这个问题,而且作者也想过解决方案,但即便是更换方位也不能解决.原话这样:
通过进一步的研究,我终于发现了问题。当进程被创建时,系统也创建一个互斥对象。每
个进程都有它自己的互斥对象,也就是说多个进程并不共享互斥对象。当线程调用映射到进程
的地址空间中的D L L的D l l M a i n函数时,这个互斥对象负责对进程的所有线程实施同步


我不知道你是如何调试的,方式又是怎么样,不过我想到的暂时是这样.
另外,回到我最早之前对你说的,想要学就学原理,光是这样乱调试,出问题也是换汤不换药
#6
tm1mc22014-09-30 16:25
谢谢回答。
我去网上书店买了<windows核心编程>,并且到百度文库下载了该书的第19-21,讲动态链接库的部分。
    目前问题是这样:我把别人能够正常调试的程序拿来修改。4个窗口是他的不是我的,我没改,如果他的是独立线程那么我的就是,反之也然。能否正常调试跟是否独立线程无关。不过从我的水平上理解,这4个窗口不是线程。这4个窗口中1个是其余3个的父窗口。它们各有自己的消息处理程序。不过扯这些没用。反正他的能正常调试。
    我只是把一个函数改为直接执行,省去调用环节而已,这几个窗口一点都没改。DLL_THREAD_ATTACH消息的处理也没改。如果我的程序是因为“新线程状态dllmain返回DLL_THREAD_ATTACH从而直接跳过上面.”,那么他的程序为什么就不会“从而直接跳过上面.”?就不会“悲剧了”?
    问题应该出在我跟他的不同点上。但原先修改太多,不同点也太多,因此简化修改:只加一句无错的废话:https://bbs.bccn.net/thread-436751-1-1.html
    结果也不行。所以你上面说的哪些原因不对。那些原因在原来能调试的程序中都有,却能正常调试。
#7
天使梦魔2014-09-30 19:58
为什么自己就不能做测试。
如果是全局变量的问题就把变量放到那个状态下,反正离开switch时就过期了。
调试的时候中断值呢?返回什么?在等待什么?是单纯的调试等待参数还是编译不过去?
内部代码是否有调试专用的debug宏?

我倒是蛮好奇,对于说的这种改动,不知能否发两份代码一份没问题的原版,一份你只改了一点点的改动版。这个论坛可以上传压缩文件
#8
tm1mc22014-09-30 20:03
请教如何把文件传给你?
#9
天使梦魔2014-09-30 20:33
发帖有上传附件
#10
tm1mc22014-10-02 11:24
路路通的软件包:
只有本站会员才能查看附件,请 登录

路路通的码表:
只有本站会员才能查看附件,请 登录


如果你的系统是XP,就把下载的词库文件py.txt拷贝到:\WINDOWS\system32。这件事最重要,否则程序运行时找不到码表会死机,甚至可能要重装系统
建一个文件夹,软件包拷进去双击它,会释放出很多文件和文件夹。把其中的文件夹“说明”中的imm.h覆盖你的VC++的旧版本中的同名文件。
然后进入:Build->set Active Configuration,选中:srf-Win32 Debug。
然后选noteped.exe来调试本Dll。
做法是:
Project->Settings->Debug->Executable for debug session
单击它下面右边的按钮,点击Browse...。对于windowsXP系统,它在C:\WINDOWS\NOTEPAD.EXE
设置好断点后,点击F5,VC就会打开记事本,在Windows的状态栏中选择你的输入法,就可以像调试普通可执行程序一样调试你的输入法了。

有点眉目了:
其它网站有人这样答复:
如果编译通过,debug是可以进断点的。
不能进断点本身应该是因为源码和实际动态库不一致
你可能有其他的程序加载了这个动态库,这样动态库就不能被替换。
所以,虽然编译通过了但是输入法对应的动态库还是之前的。
当你把代码改回去的时候源码和动态库一致就可以调试了。
实际上输入法对应的动态库从头到尾都没有变。
你可以看一下项目构建的完整输出,发出来好确认问题。还有工程的设置最好也贴一下。

我猜想他的意思是:
我用notepad.exe调试我的程序时,即使关闭那个调试的notpad窗口,或者按shift+F5退出调试,但是电脑上还有其他notepad窗口的话,那么“天下无敌”输入法仍然没有退出,因此下次再调试时系统就不会调用DllMain。

[ 本帖最后由 tm1mc2 于 2014-10-2 11:42 编辑 ]
#11
天使梦魔2014-10-02 13:12
1>.\uistatus.c(2) : fatal error C1083: 无法打开包括文件:“winable.h”: No such file or directory
哎吗。。。。悲剧了,另外
1>srf - 184 个错误,47 个警告

winable.h我是弄不到了,看了下介绍:
winable.h在2005年7月已经从Windows SDK移除,因为它的功能已经在winuser.h被复制。所以那时候决定花费更多的努力在更新winuser.h上,而这样就相当于是在更新两个文件的功能。(winable.h was moved from the Windows SDK in July 2005 because functionality was duplicated in winuser.h. It was determined at that time that efforts would be better spent on updating winuser.h to Windows Vista-level functionality rather than updating the functionality of both files.

只能算了,另外你网上找到的简直有点天方夜谭。若真如这样,那我只能说vc6编译器就是个智障。(我用的不多,vc6盛行的时候用的是mingw,后来改成vs2008)
它的意思是,假设有个exe是输出“123”
而程序代码没有cout而是cin,反汇编的时候无法从CPU指令里找到cout动作,所以无法调试。
这不扯么,哪个编译器在调试前不重新生成一次。就像我前面说的,你可以写个cout程序,编译它然后生成,接着改成cin后直接F5.
另外,不管你程序有没有退出,假如没有退出时无法进行第二次编译生成的,编译器会提示编译结果,而且我们也知道当一个程序打开占用的时候,另一个程序是无法对它写操作的,同样你可以自己测试一下,编译好一个程序后进入代码文件夹启动程序(不要关闭),接着改动源代码重新编译,提示是无法操作的。
dll存在同样道理,它的计数器不归0,内存中是不会释放的,不会释放就无法再次对硬盘里的dll写操作,也不会被删除(文件打开中无法删除)

但有一点是肯定的,调试和编译是两种概念,很早以前的时候vc系列编译器就以调试强大而出名,但被那帮做linux转到win做项目的人鄙视,因为调试和编译是完全不同的2种性质,所以C++其它编译器并没有在调试功能上下很大的功夫。
调试的时候动作等同于反汇编,记住,二进制生成和脚本代码最大的区别除了功能上的另一个是代码还原能力几乎为0.如果反汇编什么都能做那这意味任何程序都会被还原成源代码。那也意味没有技术垄断,意味着当winrar加密后只要有人反跟踪一次操作行为就可以做出解压rar不需要密码的程序。

话说回来,我倒觉得那改动的代码并不是不能被跟踪的。不过我还想不到什么原因
7楼我已经说过了,我一直以为是编译不过去(调试前会有编译重新生成行为)。我一直在想是哪里错了。
说到现在才知道你能编译,而只是不能调试某一段代码。
最大的一个可能是,在你启动调试前,这段代码已经被执行了。(我不知道它的调试方式是什么,但上面出问题的代码等于初始化dll,调试器会跟踪自身初始化代码吗?)。如果你真心想跟踪它,我推荐一款已经停掉的软件OllyDebug被称为世上最强反汇编工具,当然前提是你看得懂。
你可以试着跟踪dll的其它地方,如果就只是状态码不能调试,那就是调试器的问题。
#12
tm1mc22014-10-02 18:33
好吧该结帖了。
问题有点眉目了。
Dll进入后不易退出,因此修改编译后大概要重启才能调试。不过我记得以前有人解决过这类问题。我有空去找找看。
昨天电脑故障,今天又忙其它事。明后天估计可以把问题弄清楚。以后遇到问题再问吧。
1