作了一个小的信息管理系统,按照书上的源码输入,编译通过
但在运行时,如果要编辑一个记录的话,确定后确会报错.
基本内容如下:
Debug Assertion Failedfile:strex.cpp
Line:672一会儿编辑另一个记录又会报错说错在另一行(Line:512)
怎么回事啊??有没有调试方面的教材?

解决方案 »

  1.   

    VC调试入门 
    概述
    调试是一个程序员最基本的技能,其重要性甚至超过学习一门语言。不会调试的程序员就意味着他即使会一门语言,却不能编制出任何好的软件。
    这里我简要的根据自己的经验列出调试中比较常用的技巧,希望对大家有用。
    本文约定,在选择菜单时,通过/表示分级菜单,例如File/Open表示顶级菜单File的子菜单Open。设置
    为了调试一个程序,首先必须使程序中包含调试信息。一般情况下,一个从AppWizard创建的工程中包含的Debug Configuration自动包含调试信息,但是是不是Debug版本并不是程序包含调试信息的决定因素,程序设计者可以在任意的Configuration中增加调试信息,包括Release版本。
    为了增加调试信息,可以按照下述步骤进行: 打开Project settings对话框(可以通过快捷键ALT+F7打开,也可以通过IDE菜单Project/Settings打开) 
    选择C/C++页,Category中选择general ,则出现一个Debug Info下拉列表框,可供选择的调试信息 方式包括: 
     命令行 Project settings 说明 
    无 None 没有调试信息 
    /Zd Line Numbers Only 目标文件或者可执行文件中只包含全局和导出符号以及代码行信息,不包含符号调试信息 
    /Z7 C 7.0- Compatible 目标文件或者可执行文件中包含行号和所有符号调试信息,包括变量名及类型,函数及原型等 
    /Zi Program Database 创建一个程序库(PDB),包括类型信息和符号调试信息。 
    /ZI Program Database for Edit and Continue 除了前面/Zi的功能外,这个选项允许对代码进行调试过程中的修改和继续执行。这个选项同时使#pragma设置的优化功能无效 
    选择Link页,选中复选框"Generate Debug Info",这个选项将使连接器把调试信息写进可执行文件和DLL 
    如果C/C++页中设置了Program Database以上的选项,则Link incrementally可以选择。选中这个选项,将使程序可以在上一次编译的基础上被编译(即增量编译),而不必每次都从头开始编译。 
    断点
    断点是调试器设置的一个代码位置。当程序运行到断点时,程序中断执行,回到调试器。断点是 最常用的技巧。调试时,只有设置了断点并使程序回到调试器,才能对程序进行在线调试。设置断点:可以通过下述方法设置一个断点。首先把光标移动到需要设置断点的代码行上,然后 
    按F9快捷键 
    弹出Breakpoints对话框,方法是按快捷键CTRL+B或ALT+F9,或者通过菜单Edit/Breakpoints打开。打开后点击Break at编辑框的右侧的箭头,选择 合适的位置信息。一般情况下,直接选择line xxx就足够了,如果想设置不是当前位置的断点,可以选择Advanced,然后填写函数、行号和可执行文件信息。 
    去掉断点:把光标移动到给定断点所在的行,再次按F9就可以取消断点。同前面所述,打开Breakpoints对话框后,也可以按照界面提示去掉断点。条件断点:可以为断点设置一个条件,这样的断点称为条件断点。对于新加的断点,可以单击Conditions按钮,为断点设置一个表达式。当这个表达式发生改变时,程序就 被中断。底下设置包括“观察数组或者结构的元素个数”,似乎可以设置一个指针所指向的内存区的大小,但是我设置一个比较的值但是改动 范围之外的内存区似乎也导致断点起效。最后一个设置可以让程序先执行多少次然后才到达断点。数据断点:数据断点只能在Breakpoints对话框中设置。选择“Data”页,就显示了设置数据断点的对话框。在编辑框中输入一个表达式,当这个 表达式的值发生变化时,数据断点就到达。一般情况下,这个表达式应该由运算符和全局变量构成,例如:在编辑框中输入 g_bFlag这个全局变量的名字,那么当程序中有g_bFlag= !g_bFlag时,程序就将停在这个语句处。消息断点:VC也支持对Windows消息进行截获。他有两种方式进行截获:窗口消息处理函数和特定消息中断。
    在Breakpoints对话框中选择Messages页,就可以设置消息断点。如果在上面那个对话框中写入消息处理函数的名字,那么 每次消息被这个函数处理,断点就到达(我觉得如果采用普通断点在这个函数中截获,效果应该一样)。如果在底下的下拉 列表框选择一个消息,则每次这种消息到达,程序就中断。值
    Watch
    VC支持查看变量、表达式和内存的值。所有这些观察都必须是在断点中断的情况下进行。
    观看变量的值最简单,当断点到达时,把光标移动到这个变量上,停留一会就可以看到变量的值。
    VC提供一种被成为Watch的机制来观看变量和表达式的值。在断点状态下,在变量上单击右键,选择Quick Watch, 就弹出一个对话框,显示这个变量的值。
    单击Debug工具条上的Watch按钮,就出现一个Watch视图(Watch1,Watch2,Watch3,Watch4),在该视图中输入变量或者表达式,就可以观察 变量或者表达式的值。注意:这个表达式不能有副作用,例如++运算符绝对禁止用于这个表达式中,因为这个运算符将修改变量的值,导致 软件的逻辑被破坏。Memory
    由于指针指向的数组,Watch只能显示第一个元素的值。为了显示数组的后续内容,或者要显示一片内存的内容,可以使用memory功能。在 Debug工具条上点memory按钮,就弹出一个对话框,在其中输入地址,就可以显示该地址指向的内存的内容。Varibles
    Debug工具条上的Varibles按钮弹出一个框,显示所有当前执行上下文中可见的变量的值。特别是当前指令涉及的变量,以红色显示。寄存器
    Debug工具条上的Reigsters按钮弹出一个框,显示当前的所有寄存器的值。进程控制
    VC允许被中断的程序继续运行、单步运行和运行到指定光标处,分别对应快捷键F5、F10/F11和CTRL+F10。各个快捷键功能如下: 
     快捷键 说明 
    F5 继续运行 
    F10 单步,如果涉及到子函数,不进入子函数内部 
    F11 单步,如果涉及到子函数,进入子函数内部 
    CTRL+F10 运行到当前光标处。 Call Stack
    调用堆栈反映了当前断点处函数是被那些函数按照什么顺序调用的。单击Debug工具条上的Call stack就显示Call Stack对话框。在CallStack对话框中显示了一个调用系列,最上面的是当前函数,往下依次是调用函数的上级函数。单击这些函数名可以跳到对应的函数中去。其他调试手段
    系统提供一系列特殊的函数或者宏来处理Debug版本相关的信息,如下: 宏名/函数名 说明 
    TRACE 使用方法和printf完全一致,他在output框中输出调试信息 
    ASSERT 它接收一个表达式,如果这个表达式为TRUE,则无动作,否则中断当前程序执行。对于系统中出现这个宏 导致的中断,应该认为你的函数调用未能满足系统的调用此函数的前提条件。例如,对于一个还没有创建的窗口调用SetWindowText等。 
    VERIFY 和ASSERT功能类似,所不同的是,在Release版本中,ASSERT不计算输入的表达式的值,而VERIFY计算表达式的值。 关注
    一个好的程序员不应该把所有的判断交给编译器和调试器,应该在程序中自己加以程序保护和错误定位,具体措施包括: 对于所有有返回值的函数,都应该检查返回值,除非你确信这个函数调用绝对不会出错,或者不关心它是否出错。 
    一些函数返回错误,需要用其他函数获得错误的具体信息。例如accept返回INVALID_SOCKET表示accept失败,为了查明 具体的失败原因,应该立刻用WSAGetLastError获得错误码,并针对性的解决问题。 
    有些函数通过异常机制抛出错误,应该用TRY-CATCH语句来检查错误 
    程序员对于能处理的错误,应该自己在底层处理,对于不能处理的,应该报告给用户让他们决定怎么处理。如果程序出了异常, 却不对返回值和其他机制返回的错误信息进行判断,只能是加大了找错误的难度。 
    另外:VC中要编制程序不应该一开始就写cpp/h文件,而应该首先创建一个合适的工程。因为只有这样,VC才能选择合适的编译、连接 选项。对于加入到工程中的cpp文件,应该检查是否在第一行显式的包含stdafx.h头文件,这是Microsoft Visual Studio为了加快编译 速度而设置的预编译头文件。在这个#include "stdafx.h"行前面的所有代码将被忽略,所以其他头文件应该在这一行后面被包含。
    对于.c文件,由于不能包含stdafx.h,因此可以通过Project settings把它的预编译头设置为“不使用”,方法是: 
    弹出Project settings对话框 
    选择C/C++ 
    Category选择Precompilation Header 
    选择不使用预编译头。 
      

  2.   

    仅通过崩溃地址找出源代码的出错行
    作者:老罗
    提交者:eastvc 发布日期:2003-10-23 9:16:11
    原文出处:http://www.luocong.com/articles/show_article.asp?Article_ID=29
    作为程序员,我们平时最担心见到的事情是什么?是内存泄漏?是界面不好看?……错啦!我相信我的看法是不会有人反对的——那就是,程序发生了崩溃! 
    “该程序执行了非法操作,即将关闭。请与你的软件供应商联系。”,呵呵,这句 M$ 的“名言”,恐怕就是程序员最担心见到的东西了。有的时候,自己的程序在自己的机器上运行得好好的,但是到了别人的机器上就崩溃了;有时自己在编写和测试的过程中就莫名其妙地遇到了非法操作,但是却无法确定到底是源代码中的哪行引起的……是不是很痛苦呢?不要紧,本文可以帮助你走出这种困境,甚至你从此之后可以自豪地要求用户把崩溃地址告诉你,然后你就可以精确地定位到源代码中出错的那行了。(很神奇吧?呵呵。)首先我必须强调的是,本方法可以在目前市面上任意一款编译器上面使用。但是我只熟悉 M$ 的 VC 和 MASM ,因此后面的部分只介绍如何在这两个编译器中实现,请读者自行融会贯通,掌握在别的编译器上使用的方法。Well,废话说完了,让我们开始! :)首先必须生成程序的 MAP 文件。什么是 MAP 文件?简单地讲, MAP 文件是程序的全局符号、源文件和代码行号信息的唯一的文本表示方法,它可以在任何地方、任何时候使用,不需要有额外的程序进行支持。而且,这是唯一能找出程序崩溃的地方的救星。好吧,既然 MAP 文件如此神奇,那么我们应该如何生成它呢?在 VC 中,我们可以按下 Alt+F7 ,打开“Project Settings”选项页,选择 C/C++ 选项卡,并在最下面的 Project Options 里面输入:/Zd ,然后要选择 Link 选项卡,在最下面的 Project Options 里面输入: /mapinfo:lines 和 /map:PROJECT_NAME.map 。最后按下 F7 来编译生成 EXE 可执行文件和 MAP 文件。在 MASM 中,我们要设置编译和连接参数,我通常是这样做的:rc %1.rc
    ml /c /coff /Zd %1.asm
    link /subsystem:windows /mapinfo:exports /mapinfo:lines /map:%1.map %1.obj %1.res 把它保存成 makem.bat ,就可以在命令行输入 makem filename 来编译生成 EXE 可执行文件和 MAP 文件了。在此我先解释一下加入的参数的含义:/Zd 表示在编译的时候生成行信息
    /map[:filename] 表示生成 MAP 文件的路径和文件名
    /mapinfo:lines 表示生成 MAP 文件时,加入行信息
    /mapinfo:exports 表示生成 MAP 文件时,加入 exported functions (如果生成的是 DLL 文件,这个选项就要加上) OK,通过上面的步骤,我们已经得到了 MAP 文件,那么我们该如何利用它呢?让我们从简单的实例入手,请打开你的 VC ,新建这样一个文件:01 //****************************************************************
    02 //程序名称:演示如何通过崩溃地址找出源代码的出错行
    03 //作者:罗聪
    04 //日期:2003-2-7
    05 //出处:http://www.luocong.com(老罗的缤纷天地)
    06 //本程序会产生“除0错误”,以至于会弹出“非法操作”对话框。
    07 //“除0错误”只会在 Debug 版本下产生,本程序为了演示而尽量简化。
    08 //注意事项:如欲转载,请保持本程序的完整,并注明:
    09 //转载自“老罗的缤纷天地”(http://www.luocong.com)
    10 //****************************************************************
    11 
    12 void Crash(void)
    13 {
    14 int i = 1;
    15 int j = 0;
    16 i /= j;
    17 }
    18 
    19 void main(void)
    20 {
    21 Crash();
    22 } 很显然本程序有“除0错误”,在 Debug 方式下编译的话,运行时肯定会产生“非法操作”。好,让我们运行它,果然,“非法操作”对话框出现了,这时我们点击“详细信息”按钮,记录下产生崩溃的地址——在我的机器上是 0x0040104a 。再看看它的 MAP 文件:(由于文件内容太长,中间没用的部分我进行了省略)CrashDemoTimestamp is 3e430a76 (Fri Feb 07 09:23:02 2003)Preferred load address is 00400000Start Length Name Class
    0001:00000000 0000de04H .text CODE
    0001:0000de04 0001000cH .textbss CODE
    0002:00000000 00001346H .rdata DATA
    0002:00001346 00000000H .edata DATA
    0003:00000000 00000104H .CRT$XCA DATA
    0003:00000104 00000104H .CRT$XCZ DATA
    0003:00000208 00000104H .CRT$XIA DATA
    0003:0000030c 00000109H .CRT$XIC DATA
    0003:00000418 00000104H .CRT$XIZ DATA
    0003:0000051c 00000104H .CRT$XPA DATA
    0003:00000620 00000104H .CRT$XPX DATA
    0003:00000724 00000104H .CRT$XPZ DATA
    0003:00000828 00000104H .CRT$XTA DATA
    0003:0000092c 00000104H .CRT$XTZ DATA
    0003:00000a30 00000b93H .data DATA
    0003:000015c4 00001974H .bss DATA
    0004:00000000 00000014H .idata$2 DATA
    0004:00000014 00000014H .idata$3 DATA
    0004:00000028 00000110H .idata$4 DATA
    0004:00000138 00000110H .idata$5 DATA
    0004:00000248 000004afH .idata$6 DATA
      

  3.   

    Address Publics by Value Rva+Base Lib:Object0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj
    0001:00000070 _main 00401070 f CrashDemo.obj
    0004:00000000 __IMPORT_DESCRIPTOR_KERNEL32 00424000 kernel32:KERNEL32.dll
    0004:00000014 __NULL_IMPORT_DESCRIPTOR 00424014 kernel32:KERNEL32.dll
    0004:00000138 __imp__GetCommandLineA@0 00424138 kernel32:KERNEL32.dll
    0004:0000013c __imp__GetVersion@0 0042413c kernel32:KERNEL32.dll
    0004:00000140 __imp__ExitProcess@4 00424140 kernel32:KERNEL32.dll
    0004:00000144 __imp__DebugBreak@0 00424144 kernel32:KERNEL32.dll
    0004:00000148 __imp__GetStdHandle@4 00424148 kernel32:KERNEL32.dll
    0004:0000014c __imp__WriteFile@20 0042414c kernel32:KERNEL32.dll
    0004:00000150 __imp__InterlockedDecrement@4 00424150 kernel32:KERNEL32.dll
    0004:00000154 __imp__OutputDebugStringA@4 00424154 kernel32:KERNEL32.dll
    0004:00000158 __imp__GetProcAddress@8 00424158 kernel32:KERNEL32.dll
    0004:0000015c __imp__LoadLibraryA@4 0042415c kernel32:KERNEL32.dll
    0004:00000160 __imp__InterlockedIncrement@4 00424160 kernel32:KERNEL32.dll
    0004:00000164 __imp__GetModuleFileNameA@12 00424164 kernel32:KERNEL32.dll
    0004:00000168 __imp__TerminateProcess@8 00424168 kernel32:KERNEL32.dll
    0004:0000016c __imp__GetCurrentProcess@0 0042416c kernel32:KERNEL32.dll
    0004:00000170 __imp__UnhandledExceptionFilter@4 00424170 kernel32:KERNEL32.dll
    0004:00000174 __imp__FreeEnvironmentStringsA@4 00424174 kernel32:KERNEL32.dll
    0004:00000178 __imp__FreeEnvironmentStringsW@4 00424178 kernel32:KERNEL32.dll
    0004:0000017c __imp__WideCharToMultiByte@32 0042417c kernel32:KERNEL32.dll
    0004:00000180 __imp__GetEnvironmentStrings@0 00424180 kernel32:KERNEL32.dll
    0004:00000184 __imp__GetEnvironmentStringsW@0 00424184 kernel32:KERNEL32.dll
    0004:00000188 __imp__SetHandleCount@4 00424188 kernel32:KERNEL32.dll
    0004:0000018c __imp__GetFileType@4 0042418c kernel32:KERNEL32.dll
    0004:00000190 __imp__GetStartupInfoA@4 00424190 kernel32:KERNEL32.dll
    0004:00000194 __imp__HeapDestroy@4 00424194 kernel32:KERNEL32.dll
    0004:00000198 __imp__HeapCreate@12 00424198 kernel32:KERNEL32.dll
    0004:0000019c __imp__HeapFree@12 0042419c kernel32:KERNEL32.dll
    0004:000001a0 __imp__VirtualFree@12 004241a0 kernel32:KERNEL32.dll
    0004:000001a4 __imp__RtlUnwind@16 004241a4 kernel32:KERNEL32.dll
    0004:000001a8 __imp__GetLastError@0 004241a8 kernel32:KERNEL32.dll
    0004:000001ac __imp__SetConsoleCtrlHandler@8 004241ac kernel32:KERNEL32.dll
    0004:000001b0 __imp__IsBadWritePtr@8 004241b0 kernel32:KERNEL32.dll
    0004:000001b4 __imp__IsBadReadPtr@8 004241b4 kernel32:KERNEL32.dll
    0004:000001b8 __imp__HeapValidate@12 004241b8 kernel32:KERNEL32.dll
    0004:000001bc __imp__GetCPInfo@8 004241bc kernel32:KERNEL32.dll
    0004:000001c0 __imp__GetACP@0 004241c0 kernel32:KERNEL32.dll
    0004:000001c4 __imp__GetOEMCP@0 004241c4 kernel32:KERNEL32.dll
    0004:000001c8 __imp__HeapAlloc@12 004241c8 kernel32:KERNEL32.dll
    0004:000001cc __imp__VirtualAlloc@16 004241cc kernel32:KERNEL32.dll
    0004:000001d0 __imp__HeapReAlloc@16 004241d0 kernel32:KERNEL32.dll
    0004:000001d4 __imp__MultiByteToWideChar@24 004241d4 kernel32:KERNEL32.dll
    0004:000001d8 __imp__LCMapStringA@24 004241d8 kernel32:KERNEL32.dll
    0004:000001dc __imp__LCMapStringW@24 004241dc kernel32:KERNEL32.dll
    0004:000001e0 __imp__GetStringTypeA@20 004241e0 kernel32:KERNEL32.dll
    0004:000001e4 __imp__GetStringTypeW@16 004241e4 kernel32:KERNEL32.dll
    0004:000001e8 __imp__SetFilePointer@16 004241e8 kernel32:KERNEL32.dll
    0004:000001ec __imp__SetStdHandle@8 004241ec kernel32:KERNEL32.dll
    0004:000001f0 __imp__FlushFileBuffers@4 004241f0 kernel32:KERNEL32.dll
    0004:000001f4 __imp__CloseHandle@4 004241f4 kernel32:KERNEL32.dll
    0004:000001f8 \177KERNEL32_NULL_THUNK_DATA 004241f8 kernel32:KERNEL32.dllentry point at 0001:000000f0
    Line numbers for .\Debug\CrashDemo.obj(d:\msdev\myprojects\crashdemo\crashdemo.cpp) segment .text13 0001:00000020 14 0001:00000038 15 0001:0000003f 16 0001:00000046
    17 0001:00000050 20 0001:00000070 21 0001:00000088 22 0001:0000008d 如果仔细浏览 Rva+Base 这栏,你会发现第一个比崩溃地址 0x0040104a 大的函数地址是 0x00401070 ,所以在 0x00401070 这个地址之前的那个入口就是产生崩溃的函数,也就是这行:0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj 因此,发生崩溃的函数就是 ?Crash@@YAXXZ ,所有以问号开头的函数名称都是 C++ 修饰的名称。在我们的源程序中,也就是 Crash() 这个子函数。OK,现在我们轻而易举地便知道了发生崩溃的函数名称,你是不是很兴奋呢?呵呵,先别忙,接下来,更厉害的招数要出场了。请注意 MAP 文件的最后部分——代码行信息(Line numbers information),它是以这样的形式显示的:13 0001:00000020 第一个数字代表在源代码中的代码行号,第二个数是该代码行在所属的代码段中的偏移量。如果要查找代码行号,需要使用下面的公式做一些十六进制的减法运算:崩溃行偏移 = 崩溃地址(Crash Address) - 基地址(ImageBase Address) - 0x1000 为什么要这样做呢?细心的朋友可能会留意到 Rva+Base 这栏了,我们得到的崩溃地址都是由 偏移地址(Rva)+ 基地址(Base) 得来的,所以在计算行号的时候要把基地址减去,一般情况下,基地址的值是 0x00400000 。另外,由于一般的 PE 文件的代码段都是从 0x1000 偏移开始的,所以也必须减去 0x1000 。好了,明白了这点,我们就可以来进行小学减法计算了:崩溃行偏移 = 0x0040104a - 0x00400000 - 0x1000 = 0x4a 如果浏览 MAP 文件的代码行信息,会看到不超过计算结果,但却最接近的数是 CrashDemo.cpp 文件中的:16 0001:00000046 也就是在源代码中的第 16 行,让我们来看看源代码:16 i /= j; 哈!!!果然就是第 16 行啊!兴奋吗?我也一样! :)方法已经介绍完了,从今以后,我们就可以精确地定位到源代码中的崩溃行,而且只要编译器可以生成 MAP 文件(包括 VC、MASM、VB、BCB、Delphi……),本方法都是适用的。我们时常抱怨 M$ 的产品如何如何差,但其实 M$ 还是有意无意间提供了很多有价值的信息给我们的,只是我们往往不懂得怎么利用而已……相信这样一来,你就可以更为从容地面对“非法操作”提示了。你甚至可以要求用户提供崩溃的地址,然后就可以坐在家中舒舒服服地找到出错的那行,并进行修正。是不是很爽呢? :) 
      

  4.   

    调试代码一个AfxMessageBox("aa");就解决大部分问题了,插入到
    你觉得有可能出错的地方。
      

  5.   

    void CDemo02View::EditCurUser()
    {
    CListCtrl *ctl;
    ctl=&GetListCtrl();
    //寻找位置
    POSITION pos=ctl->GetFirstSelectedItemPosition();
    if(pos==NULL)
    {
    //没有退出
    AfxMessageBox("请先选中一条记录");
    return;
    }
    //获取当前位置
    int m_CurUser=ctl->GetNextSelectedItem(pos);
    //
    CUserDlg m_UserDlg;
    char chrTemp[21]={'\0'};
    ctl->GetItemText(m_CurUser,1,chrTemp,sizeof(char[20]));
    m_UserDlg.m_UserName=chrTemp;
    ctl->GetItemText(m_CurUser,2,chrTemp,sizeof(char[20]));
    m_UserDlg.m_UserPwd=chrTemp;
    ctl->GetItemText(m_CurUser,3,chrTemp,sizeof(char[20]));
    m_UserDlg.m_UserAble=chrTemp;
    ctl->GetItemText(m_CurUser,4,chrTemp,sizeof(char[4]));
    m_UserDlg.m_UserSex=chrTemp;
    ctl->GetItemText(m_CurUser,5,chrTemp,sizeof(char[50]));
    m_UserDlg.m_UserAddr=chrTemp;
    ctl->GetItemText(m_CurUser,6,chrTemp,sizeof(char[50]));
    m_UserDlg.m_UserDep=chrTemp;
    ctl->GetItemText(m_CurUser,7,chrTemp,sizeof(char[20]));
    m_UserDlg.m_UserTitle=chrTemp;
    ctl->GetItemText(m_CurUser,8,chrTemp,sizeof(char[50]));
    m_UserDlg.m_UserEmail=chrTemp;
    ctl->GetItemText(m_CurUser,9,chrTemp,sizeof(char[20]));
    m_UserDlg.m_UserPhone=chrTemp;
    ctl->GetItemText(m_CurUser,10,chrTemp,sizeof(char[20]));
    m_UserDlg.m_UserInfo=chrTemp;
    //update
    // m_UserDlg.DoModal(); //m_UserDlg.m_UserInfo=chrTemp;
    m_UserDlg.m_Title="修改用户信息";
    if(m_UserDlg.DoModal()!=IDOK)
    {
    return;
    }
    //如果确定了选择
    ctl->GetItemText(m_CurUser,0,chrTemp,sizeof(char[20]));
    //将用户ID转换为正数保存,后面的数据库操作已此为条件
    int m_CurUserID=atoi(chrTemp);
    CUserRecordSet m_UserRecordSet;
    try
    {
    if(m_UserRecordSet.IsOpen())
    m_UserRecordSet.Close();
    //匹配条件UserID=m_CuruserID
    m_UserRecordSet.m_strFilter.Format("UserID=%d order by UserID",m_CurUserID);
    m_UserRecordSet.Open(CRecordset::snapshot,NULL,CRecordset::none);
    //如果用户记录存在,进行修改
    if(m_UserRecordSet.Open()&&!m_UserRecordSet.IsEOF())
    {
    //设置编辑当前记录
    m_UserRecordSet.Edit();
    //开始编辑
    m_UserRecordSet.m_UserAble=m_UserDlg.m_UserAble;
    m_UserRecordSet.m_UserAddr=m_UserDlg.m_UserAddr;
    m_UserRecordSet.m_UserDep=m_UserDlg.m_UserDep;
    m_UserRecordSet.m_UserEmail=m_UserDlg.m_UserEmail;
    m_UserRecordSet.m_UserInfo=m_UserDlg.m_UserEmail;
    m_UserRecordSet.m_UserName=m_UserDlg.m_UserName;
    m_UserRecordSet.m_UserPhone=m_UserDlg.m_UserPhone;
    m_UserRecordSet.m_UserPwd=m_UserDlg.m_UserPwd;
    m_UserRecordSet.m_UserSex=m_UserDlg.m_UserSex;
    m_UserRecordSet.m_UserTitle=m_UserDlg.m_UserTitle;
    //更新到数据库中
    if(m_UserRecordSet.CanUpdate())
    {
    m_UserRecordSet.Update();
    }
    //关闭记录及
    if(m_UserRecordSet.IsOpen())
    m_UserRecordSet.Close();
    AfxMessageBox("修改成功!");
    }
    else //考虑特列,如果操作中用户信息不存在了
    {
    //close database
    if(m_UserRecordSet.IsOpen())
    m_UserRecordSet.Close();
    //提示用户
    AfxMessageBox("该记录不存在,无从修改!");
    return;
    }
    }
    catch(CDBException* e)
    {
    e->ReportError();
    return;
    }
    //如果处理到这里,那处理修改后不能马上看到结果
    //所以还要将用户信息及时更新到ListCtrl中
    ctl->SetItemText(m_CurUser,1,m_UserDlg.m_UserName);
    ctl->SetItemText(m_CurUser,2,m_UserDlg.m_UserPwd);
    ctl->SetItemText(m_CurUser,3,m_UserDlg.m_UserAble);
    ctl->SetItemText(m_CurUser,5,m_UserDlg.m_UserDep);
    ctl->SetItemText(m_CurUser,6,m_UserDlg.m_UserAddr);
    ctl->SetItemText(m_CurUser,4,m_UserDlg.m_UserSex);
    ctl->SetItemText(m_CurUser,7,m_UserDlg.m_UserTitle);
    ctl->SetItemText(m_CurUser,8,m_UserDlg.m_UserEmail);
    ctl->SetItemText(m_CurUser,9,m_UserDlg.m_UserPhone);
    ctl->SetItemText(m_CurUser,10,m_UserDlg.m_UserInfo);}这是偶得代码,其中Format函数中的一个错误已经改正,通过callstack来看,错误停在了
    if(m_UserRecordSet.Open()&&!m_UserRecordSet.IsEOF())  估计是文件打开发生错误。请问怎么调试???
      

  6.   

    if(m_UserRecordSet.Open()&&!m_UserRecordSet.IsEOF())  
    是不是要改成if(m_UserRecordSet.IsOpen()&&!m_UserRecordSet.IsEOF())