|  在VC的使用过程中,每个人或多或少都会遇到一些麻烦,而这些问题可能其他人也同样遇到过,或许还没能解决。当你发现问题根结所在时,兴奋之余,欢迎你告诉他人同类问题的解决之道。
   大家如果有新的发现,请把你的编程经验收藏在这个帖子里吧!
   该贴会被放在本版面专题中http://www.csdn.net/Subject/297/index.shtm   奖励办法:对于好的编程经验,贴主可以重新开贴,斑竹会将其加入精华或FAQ,奖励可用分(精华100分)、信誉分(FAQ主要问题解决人信誉分加5)

解决方案 »

  1.   

    我自己先捧个场:)
    VC常见入门问题总结1:fatal error C1010: unexpected end of file while looking for precompiled header directive该如何解决
        如果发生错误的文件是由其他的C代码文件添加进入当前工程而引起的,则Alt+F7进入当前工程的Settings,选择C/C++选项卡,从Category组合框中选中Precompiled Headers,选择Not Using Precompiled headers。确定
        如果发生错误的文件原本是该工程中的,则检查该文件头部有没有#include "stdafx.h"语句,没有的话添加。
        如果还不行,也有可能是定义的类或结构体等最后忘了加分号,注意一下。
    2:fatal error RC1015: cannot open include file 'afxres.h'.该如何解决
        #include "afxres.h"语句是在.rc文件中的,而afxres.h文件在VC的安装目录中的.\VC98\MFC\INCLUDE目录中,所以着重查一下Tools菜单中Options对话框中的Directories中的包括文件的路径是否正确,是否在VC的安装路径中,不是的话,改过来,如果这方面没问题,则到其他机器中拷贝afxres.h到相应的目录中
    3:Dll分配的内存块,应用程序释放,结果报异常。
        用GlobalAlloc()代替new, 用GlobalFree() 代替delete就不会出错了
        其实还有一个办法,就是把dll的Settings的C/C++选项卡的Code Generation的Use Run-time liberary改成Debug Multithreaded DLL,在Release版本中改成Multithreaded DLL,就可以直接使用new和delete了,没问题
        比较规范点的做法一般是DLL分配的内存由DLL释放。在DLL中加一个函数释放内存不是更好吗。
    4:发现打印预览的图形明显比屏幕显示图形小,怎么办?
        这多半是CDC映射模式的选择引起的,缺省状态下,选择的是MM_TEXT模式,MM_TEXT以设备的像素点为单位,而不同设备的像素点的大小不同,打印机的分辨率比显示器要高很多,所以导致同样图形在打印时候变小。解决之道是统一使用其他定长的映射模式,比如MM_HIMETRIC等等(CDC::SetMapMode()改变映射模式)
    5:CString、char*、string、int、_bstr_t、CTime、COleDateTime等等的相互转换,如何判断一个字符串是一个浮点数?#include<string>
    using namespace std;
    #include <COMDEF.H>{
    CString strCString="ABC";
    char strchar[256],*pstr;

    pstr=(LPSTR)(LPCTSTR)strCString; //CString---->char*
    strcpy(strchar,(LPSTR)(LPCTSTR)strCString); //CString---->char[]

    _bstr_t strbstr=pstr; //char*---->_bstr_t
    WCHAR *strWCHAR=strbstr; //b_str_t--->UNICODE strbstr=strWCHAR;
    pstr=strbstr; //UNICODE---->char* strCString="10";
    int istr=atoi((LPSTR)(LPCTSTR)strCString); //CString、char[]、char*------>int
    strCString.Format("%d",istr); //int----->CString
    sprintf(strchar,"%d",istr); //int----->char[]

    pstr=new char[256]; //字符串申请空间
    strcpy(pstr,"ABC"); //字符串赋值
    delete []pstr; //字符串释放

    string strstring="ABC";
    pstr=(char*)strstring.c_str(); //string---->char* strCString="2003-10-27 6:24:37"; //CString--->COleDateTime
    COleVariant vtime(strCString);
    vtime.ChangeType(VT_DATE);
    COleDateTime time4=vtime;

    COleDateTime time1(1977,4,16,2,2,2); //COleDataTime--->CTime
    SYSTEMTIME systime;
    VariantTimeToSystemTime(time1, &systime);
    CTime tm(systime); time_t time2=tm.GetTime(); //CTime--->time_t
    COleDateTime time3(time2); //time_t--->COleDateTime //判断字符串是否是某种类型
    CString sValue("123.1");
    COleVariant vValue(sValue);

    BOOL bStrIsFloat = (SUCCEEDED(VariantChangeType(&vValue, &vValue, 0, VT_R8)) && sValue.Find('.') != -1);
    if(bStrIsFloat)
    {
    AfxMessageBox("浮点");
    }
    }6:如何建立一个UNICODE应用程序?建立一个应用程序,打开Alt+F7 settings选项,选择C/C++选项卡,在Preprocessor definenation中加上_UNICODE,在Link选项卡中,在Category选择框中选择Output,在Entry-point symbol编辑框中,添加wWinMainCRTStartup确定。注意调试UNICODE程序时,需要在安装时VC选择所有选项,否则会缺少动态库和相应的.lib文件 
    7:ADO操作数据库表,更新出现问题
    在打开数据库前,添加如下语句试一下pRecordSet->CursorLocation = adUseClient;
      

  2.   

    #include "http://expert.csdn.net/Expert/topic/2164/2164574.xml?temp=.7247583"
      

  3.   

    我加一点,很多朋友感到内存泄漏问题不好解决,就此提点建议:
      分配内存和释放内存要成对出现。分配时用GlobalAlloc的,用GlobalFree释放。使用new分配的用delete释放(若new的是数组,则用delete[]释放)。用alloc分配的,用free释放。
      还有,分配时得到的指针时,释放时指针的值一定还是它。
      例外,若分配的内存有别的地方释放,则可以相应释放不显示出现。
      

  4.   

    正在学习使用全中文MSDN!
    包你无敌!
      

  5.   

    Good Idea!
    Strongly Support!
      

  6.   

    IntefOptics.lib(IntefOptics.dll) : error LNK2005: "public: bool __thiscall CIntvnOptics::NewtonRingsCreate(int,double,int,int,int,int)" (?NewtonRingsCreate@CIntvnOptics@@QAE_NHNHHHH@Z) already defined in IntvnOptics.obj这类错误是什么导致? 不要说rebuild all, 因为我已经做了不下百次了
      

  7.   

    LIB里面有和IntvnOptics.cpp里面一样的函数实现。估计你在编译库的时候将NewtonRingsCreate的实现写在头文件里面,然后你使用库的那个CPP文件include了那个头文件。
    ll.h
    //
    void func()
    {
    //...
    }
    ll.cpp
    #include "ll.h"
    以上两个文件编译成lib,使用库的时候
    ul.cpp
    #include "ll.h"
    这时候ul里面也连接了func,lib里面也有func。
    解决方法:
    1、实现放到CPP里面
    或者
    2、使用inline修饰
    inline void func()
    {
    //...
    }
      

  8.   

    现在深刻体会到 new/delete ,内存管理,算法空间,时间复杂度,流程控制等的重要性!
    待我这个项目结束了 我也学习斑竹,写一下个人的心得,与大家共享.
      

  9.   

    大家都发表一点心得吧.
    关说不做,唧唧喳喳的怎么支持楼主啊?
    这就我的一个相关的帖子.强烈支持楼主.
    http://expert.csdn.net/Expert/topic/2247/2247433.xml?temp=.6211359
      

  10.   

    另外,个人觉得VC不是一个好东东.
    学它主要是为了提高一点能力.要是只知道跟着MS小心中弹.
    大伙儿还是得先去把数据结构,编译原理,操作系统学好。
    还得学好哲学,历史,数学,工程学,经济学,美学.......哈哈,我瞎掰了.
      

  11.   

    OK,多谢各位的支持,总结编程经验光靠我一个人是不行的,希望大家也尽力参与进来
    ejiue(车马炮、将士相、卒杀)的文章摘抄如下,原贴将加入FAQ和精华区域,所以呢,ejiue会增加110可用分,5点信誉分,这就是奖励,也许太少,但略表心意,不过希望以后各位能自己把文章摘抄下来,以防丢失:)大家分享一下如何用MFC做实际项目的经验
    希望大家一起分享一下如何用MFC做实际项目的经验,中秋凑个热闹。呵呵。
    1、界面风格和控件
    2、消息循环、多线程、数据库相关
    3、数据结构和算法相关
    4、其他初学MFC的时候,大家最先接触的就是界面吧。
    虽然说界面不是特别重要,但确实让人头痛。
    弄来弄去,大多是控件搞不定。(在这个版这样的问题好多:))
    至于消息循环机制,我觉得我们用到不多。
    MFC都帮我们做好了。不过有一些注意事项。
    上一次我做多线程的时候死锁了,阿行大哥就是用消息机制帮我解决的。
    另外,rtdb大哥还赠了我1000分,使我能够继续提问。(太感谢了)
    这个版好心人不少。感谢之余,我在这里也和大家分享一下。1、
    刚开始的时候,为了把界面做得好看,
    我在www.codeproject.com和www.codeguru.com上面找到了一些好用的控件。
    如ButtonST、TruecolorToolbar、GradientProgressCtrl、CoolToolbar等
    控件。可能大家也用过。它们都很好用,而且有演示程序下载,边学边用,
    用对比法和增减法,这两个方法学MFC挺管用的。
    后来在www.vkbase.com看到了一个换肤的ActiveX控件,很好用。
    做出来的界面特好看。大家去下载看看。
    可惜对组合框等控件支持不够。(可能是小弟不会:))后来做数据库的时候,我公司的高手说选择越简单的控件越好。
    我刚开始用DataGrid,直接绑定数据库。
    他们建议用原始的Grid控件,独立于具体的数据库。
    Grid控件没有(也不需要!)编辑和组合框支持。编辑的时候跳出对话框来解决。现在我做的一个项目的界面是单文档多个formview切换。(这个界面挺古老的吧)
    曾经用过属性页控件,现在用了Tab控件和对话框嵌入,感觉这个挺好的。遇到控件问题的时候,我常看msdn,上面的实例真好。
    现在有问题来msdn,我都是先搜索一下,其实大多数问题都已经有帖子解答。
    这样可以节省自己和别人的时间。
    记住每个控件都是一个窗口是有帮助的。2、
    多线程的时候尤其要注意一个问题:MFC帮我们开的线程是界面线程。
    如果我们以后再开的线程是工作者线程,那么界面主线程不能阻塞。
    如果界面主线程阻塞了,相当于消息泵阻塞了。
    我上次犯的错误就是让其他工作者线程发一个消息来让主线程解除阻塞。结果死锁。
    解决方法是将其他线程改为界面线程。
    还有一个问题,创建新线程的时候要小心,它有一个CRuntimeClass*的参数。
    它要CreateObject()创建一个新的线程,然后把线程指针返回。
    CSerialPortEx* g_pPort = new CSerialPortEx;
    CRuntimeClass* IOClass  = g_pPort->GetRuntimeClass();
    g_pPort = (CSerialPortEx*)AfxBeginThread(IOClass);
    上面的代码将使g_pPort无法访问CSerialPortEx内部的变量。
    通过增加一个临时变量来解决。如下:
    CSerialPortEx* TempPort = new CSerialPortEx;
    CRuntimeClass* IOClass  = TempPort->GetRuntimeClass();
    g_pPort = (CSerialPortEx*)AfxBeginThread(IOClass);
    delete TempPort;
    类似的场合不少。如在用对话框嵌入标签的时候再一次创建了对话框。
    出现了实际上存在两个对话框,无法访问原来对话框上的控件。
    现在我一看到CRuntimeClass*就会特别小心。
    我在解决这些问题的时候用的方法是调试和增减法。并不是从书上得来。
    如果有差错,请各位指出来。呵呵。
    还有一个问题是进度条对话框上的控件不能及时显示,(消息循环暂时受阻塞)
    阿行大哥用在用户代码中加入消息循环解决。也可以开一个线程。数据库我都没有怎么做过。我的项目不大。
    想请教各位设计的时候需要什么考虑,还有上面说的“使用简单的控件”是否合理。
    有没有必要自己写一个数据库类,用SQL语句进行访问。3、
    数据结构我学得太烂了,现在非常后悔。不过只得加把劲了。
    幸亏我现在只用到链表和数组。呵呵。
    我觉得链表和数组都有欠缺。
    象我的项目,串口数据在不停的上来,根据上来的数据监控下面机器的状态。
    如果用数组,长度是固定的。
    如果用链表,有可能用户暂时不在监控界面。(假设长达几个小时)
    几个小时之内的数据用链表一直来存储着。
    然后等用户一切换回监控,就一下子从头到尾刷新?!
    如果什么都不用,直接用后一个状态覆盖前一个,那么就无法显示一系列的过程。
    正在考虑用一个固定长度的链表。4、
    还有其他没考虑到的地方,大家提出来讨论吧。vc做程序最重要的就是小心,小心再小心,每做一步自己都要多运行几遍,分别在debug,release模式下运行运行,
    看看有没有内存泄漏,越界等错误,等到最后才发现那就惨了1。程序运行稳定,尽量用老办法,不了解的函数不要随便用。
    2。功能要简单实用。
    3。不要追求功能的完美。完美的功能是不可能的。
    4。程序一有bug,必须纠正后才能前进
      

  12.   

    我很丑,可是我很温柔。我很温柔,不过我很丑。10进制:0-9,只有十进制的才有正负之分。
    16进制:以0x或0X开头,0-9+a-f(A-F)
    8进制:以0开头,0-7
    默认的数是double型,有f\F表示float型,有l\L表示long double型。C语言中,#include “stdio.h”
    格式控制符:
    d:十进制
    o:八进制
    x:十六进制
    c:单个字符
    f:浮点数(double\float)
    e\E:浮点数(输入和f一样,科学计数发输出)
    s:以’\0’结束的字符串
    u:十进制无符号
    g:f\e中较短的一种指定字符宽:
    *:删除空格前剩余数
    0:前面补0,而不是补空格
    格式控制:%  +/-  m  .  n  l/h  □
    +/-:对齐方式,默认+,右对齐;-左对齐。
    m:输出宽度(float时候包含小数点),大于时按实际输出,小于时补空格
    . :小数点,float\double时候才用
    n:小数点后面的位数,float\double时候才用
    l/h:表示是long\shot
    □:格式控制符d\o\x\c\f\e\s\u\g
      

  13.   

    设置窗口的透明度(win2000以上)
    -----------------------------------
    第一步 定义功能typedef BOOL (FAR PASCAL * FUNC1)(
      HWND hwnd,           // handle to the layered window
      COLORREF crKey,      // specifies the color key
      BYTE bAlpha,         // value for the blend function
      DWORD dwFlags        // action
    );
    第二步 实现代码在OnInitDialog中加入下列代码(如果在SDI里面,应该是在OnCreat里面添加).....
     HMODULE hModule = GetModuleHandle("user32.dll");
     FUNC1 SetLayeredWindowAttributes;
     SetLayeredWindowAttributes =  (FUNC1) GetProcAddress (hModule, _T( "SetLayeredWindowAttributes" ) ); // 设置分层扩展标记
     SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE) | 0x80000L);
     // 70% alpha
     SetLayeredWindowAttributes(GetSafeHwnd(), 0, (255 * 70) / 100, 0x2);
    工作完成,怎么样,现在你可以运行你的程序来查看效果,即使背景变化也能立刻反映到你的窗口当中,这一点比金山词霸的效果要好。第三步:如何除去透明选项? // 除去分层扩展标记
    SetWindowLong(GetSafeHwnd(),, GWL_EXSTYLE,
            GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE) & ~ 0x80000L);
     // 重画窗口
    RedrawWindow();
      

  14.   

    各位大哥真是说的好!写程序要有一个好的格式。
    要知道计算机是最笨的,人的比计算机聪明,写出来的程序要自己先运行。
    不要遇到一点困难就不想搞了,其实你再坚持一下就很简单了。
    也不要老是抱怨vc难,其实也没什么。年轻人应该能吃苦!我提个问题把。
    现在是用vs.net开发windows程序好,还是用vs6.0好?
      

  15.   

    还请指教:  release模式是怎样的一种模式,
    我还不懂这个名称的含义.
    至于MFC现在正在学习中,我以前是学UNIX/Linux系统编程的,请问大侠们学MFC编程要怎样才可以学得最快呢?
    在线恭候指教!!!
    先谢谢啦.
      

  16.   

    斑竹很负责任啊。
    技术方面偶的水平差的很,就不说了。以下是偶的一点经验之谈:1、调试程序的时候有简单到复杂:碰到程序运行出错时,可以先把一些程序注释掉,只留很少的一部分,确保程序的正确运行,然后再逐段逐段代码的释放,很容易发现问题所在。2、单独做试验:对程序中用到的一些独立的模块和功能,如果不是太熟悉或则把握不准,可以单独的写一个小的程序去做试验,验证用法以及结果。不要在很大的工程中调试某些独立的模块。3、仔细认真地作试验。在研究一个新的控件或功能时,要象对待正式程序一样认真的对待,否则如果出现错误,你就有可能得出一个错误的结论,一旦用到整个的程序中就会是一个很难发现的错误,浪费大量的时间去查找其他的代码。4、有时候错误的程序会莫名其妙的运行正常,但你并没有找到出错的原因。这时千万不要高兴太,应该继续调试和试验,一定要把错误找出来。只要你找不出来,这个错误就是一个炸弹,不一定什么时候发作。5、进行数据库操作的时候,碰到比较复杂的SQL语句,最好先在数据库中执行测试你的SQL语句,以保证该语句的正确性。6、建议在操作数据库的时候使用“事务处理”,否则程序在运行时(特别在网络中)会遇到意想不到的问题,如:网络不稳定可能会使你的操作只执行一部分。7、长期连续运行的程序中,一些复杂的功能不要在定时器(OnTimer)中完成,而尽量使用线程完成,我的感觉是,放在线程中要比定时器稳定的多。(只是我自己的看法)
      

  17.   

    #include the .h :#definfe yintongshun me 
      

  18.   

    谢谢bluebohe (薄荷) !
    对我帮助挺大。
    我也凑凑热闹:
    下面是一个VC调试观察窗口的格式化符号表格,希望对大家有帮助
    ——————————————————————————————————
    符号          格式                例子          输出
    d或者i      有符号十进制整数     -42,d         -42
    U           无符号十进制整数     42,d          42
    O           无符号八进制整数     42,o          052 
    x或X        十六进制整数         42,x           0x0000002a或0x0000002A
    H           为d,I,u,o,x显示前缀  42,hx          0X002a
    F           有符号浮点数         1.5,f         1.500000
    E           有符号科学计数法     1.5,e          1.500000e+000
    G           压缩的有符号浮点数   1.5,g          1.5
    C           字符                 42,c           '*'
    S           ANSI字符串           "bugs",s       "bugs"
    Su          Unicode字符串        "bugs",st      "bugs"
    Hr          HRESULT和Win32错误码 0X06,hr        The handle is invalid
    wm          Windows消息号        0x01,wm        WM_CREATE
    [digits]    显示数组元素         s,5            显示s[]前五个值
      

  19.   

    学好MFC, 可以参看SDK, 我有书...
      

  20.   

    自己水平有限 就奉献一篇文章表示支持
    也希望大家以后多多帮助
    AND_CATCHAND_CATCH 
    AND_CATCH(exception_class,exception _object_point_name) 
    说明:定义一个代码块,它用于获取废除当前TRY块中的附加异常类型。使用CATCH宏以获得一个异常类型,然后使用AND_CATCH宏获得随后的异常处理代码可以访问异常对象(若合适的话)已得到关于异常的特别原因的更多消息。在AND_CATCH块中调用THROW_LAST宏以便把处理过程移到下个外部异常框架。AND_CATCH可标记CATCH或AND_CATCH块的末尾。 注释: 
    AND_CATCH块被定义成为一个C++作用域(由花括号来描述)。若用户在此作用域定义变量,那么记住他们只在此作用域中可以访问。他也用于exception_object_pointer_name变量。  
       
    ASSERT 
    ASSERT(booleanExpression) 
    说明: 
    计算变量的值。如果结构的值为0,那么此宏便打印一个诊断消息并且成讯运行失败。如果条件为非0,那么什么也不做。 诊断消息的形式为: assertion failed in file in line 其中name是元文件名,num是源文件中运行失败的中断号。 在Release版中,ASSERT不计算表达式的值也就不中断程序。如果必须计算此表达式的值且不管环境如何那么用VERIFY代替ASSERT。 
    注释: 
    ASSERT只能在Debug版中用 
      
    ASSERT_VAILD 
    ASSERT_VAILD(pObject) 
    说明: 
    用于检测关于对象的内部状态的有效性。ASSERT_VALID调用此对象的AssertValid成员函数(把它们作为自己的变量来传递)。在Release版中ASSERT_VALID什么也不做。在DEBUG版中,他检查指针,以不同于NULL的方式进行检查,并调用对象自己的AssertValid成员函数。如果这些检测中有任何一个失败的话,那么他会以与ASSERT相同的方法显示一个警告的消息。 
    注释: 
    此函数只在DEBUG版中有效。 
      
    BEGIN_MESSAGE_MAP 
    BEGIN_MESSAGE_MAP(the class,baseclass) 
    说明: 
    使用BEGIN_MESSAGE_MAP开始用户消息映射的定义。在定义用户类函数的工具(.cpp)文件中,以BEGIN_MESSAGE_MAP宏开始消息映射,然后为每个消息处理函数增加宏项,接着以END_MESSAGE_MAP宏完成消息映射。 
      
    CATCH 
    CATCH(exception_class,exception_object_pointer_name) 
    说明: 
    使用此用定义一个代码块,此代码用来获取当前TRY块中都一个异常类型。异常处理代码可以访问异常对象,如何合适的话,就会得到关于异常的特殊原因的更多消息。调用THROW_LAST宏以把处理过程一下一个外部异常框架,如果exception-class是类CExceptioon,那么会获取所有异常类型。用户可以使用CObject::IsKindOf成员函数以确定那个特别异常被排除。一种获取异常的最好方式是使用顺序的AND_CATCH语句,每个带一个不同的异常类型。此异常类型的指针由宏定义,用户不必定义。 
    注释: 
    此CATCH块被定义作一个C++范围(由花括号描述)。如用户在此范围定义变量,那么它们只在吃范围内可以访问。他还可以用于异常对象的指针名。 
      
    DEBUG_NEW 
    #define new DEBUG_NEW 
    说明: 
    帮助查找内存错误。用户在程序中使用DEBUG_NEW,用户通常使用new运算符来从堆上分配。在Debug模式下(但定义了一个DEBUG符号),DEBUG_NEW为它分配的每个对象记录文件名和行号。然后,在用户使用CMemoryState::DumpAllObjectSince成员函数时,每个以DEBUG_NEW分配的对象分配的地方显示出文件名和行号。 为了使用DEBUG_NEW,应在用户的资源文件中插入以下指令: #define new DEBUG_NEW 一旦用户插入本指令,预处理程序将在使用new的地方插入DEBUG_NEW,而MFC作其余的工作。但用户编译自己的程序的一个发行版时,DEBUG_NEW便进行简单的new操作,而且不产生文件名和行号消息。 
      
    DECLARE_DYNAMIC 
    DECLARE_DYNAMIC(class_name) 
    说明: 
    但从CObject派生一个类时,此宏增加关于一个对象类的访问运行时间功能。把DECLARE_DYNAMIC宏加入类的头文件中,然后在全部需要访问词类对象的.CPP文件中都包含此模块。如果像所描述那样使用DELCARE_DYNAMIC和IMPLEMENT_DYNAMIC宏,那么用户便可使用RUNTIME_CLASS宏和CObject::IsKindOf函数以在运行时间决定对象类。如果DECLARE_DYNAMIC包含在类定义中,那么IMPLEMETN_DYNAMIC必须包含在类工具中。 
      
    DECLARE_DYNCREATE 
    DECLARE_DYNCREATE(class_name) 
    说明: 
    使用DECLARE_DYNCRETE宏以便允许CObject派生类的对象在运行时刻自动建立。主机使用此功能自动建立新对象,例如,但它在串行化过程中从磁盘读一个对象时,文件及视图和框架窗应该支持动态建立,因为框架需要自动建立它。把DECLARE_DYNCREATE宏加入类的.H文件中,然后在全部需要访问此类对象的.CPP文件中包含这一模式。如果DECLARE_DYNCREATE包含在类定义中,那么IMPLEMENT_DYNCREATE必须包含在类工具中。 
      

  21.   

    各位都是牛人,我刚刚开始从VB转入VC,正在写一个软件,设想是使用多文档、多视图界面,但是每个文档/视图并不是由“文件/新建”菜单生成的,而是由另一个独立的对话框中的代码生成的,并且,生成之后要直接对不同的文档进行不同的初始化,而且不是预先设计好的,这个结构的程序该如何设计呢?PS:补充个人一点小小的经验,就是做一个相对比较大的项目的时候,尽量把这个项目分解成一个个的子项目,并分别对每一个子项目进行编码、测试,分别设计测试平台、测试数据,然后,再递增的将他们进行组合测试,发现错误就立即修改,直到修改完成才进行下一步的组合测试,最后再进行界面开发将各个子项目整合起来,这样会少很多的麻烦。还有就是,前期设计真的非常重要啊!!!!!!!!
    我这个软件就是,由于前期设计不到位,出于赶时间考虑,只进行了大概的功能设计就开始开发了,结果就是,不停的发现需要新的功能,然后不停的增加新功能,同时也就不停的带来新的BUG,然后就是无休止的“测试<->修改”,最后形成的代码……不说也想象得到是个什么样子:一个又一个的临时变量,结构上的不清不白等等。最后要说的就是,一定要注意设计风格的统一,以前用VB的时候,并没有太注意这个,现在到了VC,发现了这个问题的必要性,如果风格不统一,就会造成后期维护上付出巨大的代价,因为要考虑当初设计的目的和原因等等,真得很浪费时间啊同志们!!!
      

  22.   

    好久不用vc了,当初我也有这种想法
    自己曾经整理了超过1000条的faq
    但是现在都用不到了
      

  23.   

    This is an example on how to implement a floating window application. I have used a timer control, in combination with the Point class to specify the position of the main form in the desktop. Combining those two elements I simulated the floating behavior of the main form re-defining its position on every Tick event fired by the Timer control. 比如要让“d:\move\debug\move.exe"自动运行,只要用下面这个API函数就行了!WritePrivateProfileString(_T("windows"),_T("load"),"d:\\move\\debug\\move.exe",_T("C:\\windows\\win.ini"));
    VC 和 MFC 的一些常见问题  
    --------------------------------------------------------------------------------
     
    Q:如何抛出(throw)由CUserException派生的异常? A:当我试图捕获(catch)一个派生类异常时,我得到以下错误"error C2039:'classCMyException': is not a member of 'CMyException' 'classCMyException': undeclared identifier 'IsKindOf': cannot convert parameter 1 from 'int*' to 'const struct CRuntimeClass*" 
    你必需通过使用DECLARE_DYNAMIC()和IMPLEMENT_DYNAMIC()宏来使你的CMyException类可以动态地创建。CATCH宏希望能够得到关于被抛出类的运行时刻信息。 
    异常类一定要从CUserException中派生出来吗? 不,CUserException中的"User"仅仅指用户产生的异常。而把它当作你所能派生的唯一异常是种常见的误解。 Q:如何从hDC建立一个CDC类? 
    有时Windows API将会给你一个DC句柄,你可以通过它建立一个CDC类。例如:下拉式列表、组合框和按钮。通过hDC你将接收到绘制消息。下面是将hDC转换成你更熟悉的CDC的程序段。你也可以将该技巧用在其他任何MFC类和Windows句柄的转换中。 void MyODList::DrawItem(LPDRAWITEMSTRUCT lpDrawItem)
    {
        CDC myDC;
        myDC.Attach(lpDrawItem->hDC);
        //在此插入其他需要的代码。    //如果你不将句柄分离,它将被删除,从而导致问题。
        myDC.Detach();
    }
    另一个方法是调用CDC类的FromHandle方法:
                CDC * pDC = CDC:FromHandle(lpDrawItem->hDC);
    目前还不清楚哪种方法更优越―使用FromHandle()的错误也许会更少些,因为它不要求你分离(detach)句柄。 Q:如何从磁盘上读取256色位图文件? 
    A:当前,MFC并不支持直接读取和显示DIB文件和BMP文件。然而,有很多样例应用程序能够说明如何完成该项任务。第一个例子是MFC样例程序DIBLOOK。样例MULTDOCS用DIBLOOK提供的相同源代码来读取并显示DIB文件和BMP文件。其他两个VC++中附带的例子是SDK软件包中的DIBVIEW程序和SHOWDIB程序。 Q:如何改变一个视图的大小? 
    A:通常,你可以调用函数MoveWindow()或者SetWindowPos()来改变窗口的大小。在用MFC库开发的应用程序中, 视图是被框架窗口所围绕的一个子窗口。为了改变一个视图的大小,你可以通过调用函数GetParentFrame()来得到框架窗口的指针,然后调用函数MoveWindow()/SetWindowPos()来改变父窗口的大小。当父框架窗口改变大小时,视图也会自动地改变大小来适应父窗口。 Q:如何改变一个CFormView的大小? 
    A:要想详细了解的话,你可以看有关Visual C++基础知识的文章:HOWTO: Use CFormView in SDI and MDI Applications。基本上,在从CFormView类派生出来的类中,你必须覆盖函数OnInitialUpdate()(因为CFormView::public CScrollView)。其他有关建立CFormView的细节问题,可以从该文章中获得。 /*-----------------------------------------------
    SUMMARY
    The CFormView class provides a convenient method to place controls into a view that is based on a dialog box template. 
    MORE INFORMATION
    The following steps describe how to create an AppWizard generated application using the CFormView as the default view. Use the AppWizard to generate an SDI or MDI application skeleton, stopping at step six of the AppWizard. 
    At step 6 of the AppWizard, select the view class and specify CformView as the base class using the Base class combo box. This will insert a dialog template with the proper styles set for your project's resource file. Override the OnUpdate() member function and call UpdateData() as documented in the CFormView documentation to update the member variables with the current document data and to perform dialog data exchange (DDX). NOTE: UpdateData is not virtual and calling the base class ensures that the derived class DoDataExchange is called through standard polymorphism. The CFormView documentation states to call, not override UpdateData. If you would like to set the initial size of the form view, override the OnInitialUpdate() function. The text below provides additional information about this step, which is slightly different in an SDI or MDI application. Changing the Size of an SDI Main Frame Around a CFormViewTo change the size of the main frame of an SDI application (that uses CFormView as its view class) to be the appropriate size for the form you designed in App Studio, override the OnInitialUpdate() function in your class derived from CFormView, as follows: 
          void CMyFormView::OnInitialUpdate()
          {
             CFormView::OnInitialUpdate();
             GetParentFrame()->RecalcLayout();
             ResizeParentToFit(); // default argument is TRUE
          } 
    The ResizeParentToFit() function does not prevent the form from changing size when the user changes the size of the application main frame (scroll bars are added automatically if needed). To modify the style of the frame window that is the parent of a form view, you can override the PreCreateWindow() function in the CMainFrame class generated by AppWizard. For example, to remove the WS_THICKFRAME style and prevent the user from changing the size of the window, declare PreCreateWindow() in MAINFRM.H and add the following code to MAINFRM.CPP:       BOOL CMainFrame::PreCreateWindow(CREATESTRUCT &cs)
          {
             cs.style &= ~WS_THICKFRAME;
             return CFrameWnd::PreCreateWindow(cs);
          } 
          
    Changing the Size of an MDI Child Frame Around a CFormViewThe process of changing the size of an MDI child frame is similar to changing the size of a main frame for an SDI application, as explained above. However, the RecalcLayout() call is not required. To change the size of an MDI child frame around a form view, override the OnInitialUpdate() function in your class derived from CFormView as follows:       void CMDIFormView::OnInitialUpdate()
          {
             CFormView::OnInitialUpdate();
             ResizeParentToFit(); // Default argument is TRUE.
          } 
    If the application overrides the default argument to the ResizeParentToFit() function, essentially the same consequences occur as for an SDI application, as explained above. In addition, the child window may be too large for the enclosing MDI main frame or for the entire screen. To change the style of the MDI child frame (for example, to remove the WS_THICKFRAME style so the user cannot change the size of the window), derive an MDI child window class and override the PreCreateWindow function as demonstrated in the SDI example above. 
    Closing an MDI Form with a Button
    To create a button on a form that closes the document, use ClassWizard to add a message handler for the BN_CLICKED message to the CFormView class. Make sure that the buttons in CFormView do not have the default IDOK or IDCANCEL identifiers. If they do, ClassWizard creates incorrect entries in the message map and incorrect functions for the buttons. Once the message handler is in place, you can simulate the Close command on the File menu with the following code:       void CMyForm::OnClickedButton1()
          {
             PostMessage(WM_COMMAND, ID_FILE_CLOSE);
          } 
    This method to close a form prompts the user to save the file if the IsModified() member function associated with the document returns TRUE. 
    */------------------------------------------------
    在类ClikethisView中声明如下函数:
      virtual void OnInitialUpdate();在ClikethisView的代码中,函数如下:  void ClikethisView::OnInitialUpdate()
      {
          //使窗口与主对话框同样大小
          CFormView::OnInitialUpdate();
          GetParentFrame()->RecalcLayout();
          ResizeParentToFit( /*FALSE*/ );//must be false;
      }
      

  24.   

    Q:如何在一个文档中建立多个视图? 
    A:CDocTemplate::CreateNewFrame()函数创建MFC MDI应用程序中的文档的附加视图。为了调用该函数,要指定一个指向CDocument对象(指将为之建立视图的文档)的指针和一个指向可从中复制属性的框架窗口的指针。一般情形下,该函数的第二个参数为NULL。 当应用程序调用函数CreateNewFrame()时,该函数就创建一个框架窗口和在该窗口内的视图。框架窗口和它的视图的类型由与CreateNewFrame()函数调用指定的文档相关的文档摸板(CDocTemplate)决定。 CreateNewFrame()函数建立了一个框架和一个视图,而不仅仅是一个视图。假如CreateNewFrame()函数不能完全符合你的需要,可参考CreateNewFrame()函数的源程序来了解对建立结构和视图所必须的步骤。 
    辅助函数:
    ///////////////////////////////////////////////////////////
    // Create a new view based on the document template
    // parameter and associated with the given document objectCFrameWnd* UserCreateNewWindow( CDocTemplate* pTemplate,
                                  CDocument* pDocument )
    {
    ASSERT_VALID( pTemplate );
    ASSERT_VALID( pDocument ); // 1 - Create the new frame window
    // (will in turn create the associated view)
    CFrameWnd* pFrame = pTemplate->CreateNewFrame(pDocument, NULL );
    if( pFrame == NULL )
    {
    // Window creation failed
    TRACE0( "Warning: failed to create new frame.\n" );
    return NULL;
    }
    ASSERT_KINDOF( CFrameWnd, pFrame ); // 2 - Tell the frame to update itself
    // (and its child windows)
    pTemplate->InitialUpdateFrame( pFrame, pDocument ); // 3 - Return a pointer to the new frame window object
    return pFrame;
    }Q:如何在MDI程序中得到所有的视图? A:你必须用一些文档中没有记载的函数:   CDocument::GetFirstViewPosition(); // DOCCORE.CPP
      CDocument::GetNextView(); // DOCCORE.CPP
      CMultiDocTemplate::GetFirstDocPosition(); // DOCMULTI.CPP
      CMultiDocTemplate::GetNextDoc(); // DOCMULTI.CPP 
      你还需要与CWinApp的成员m_templateList打交道。
    注意:在MFC 版本4.0中已改变。现在已经有一个叫CDocManager的类可以帮助你显示所有的视图和文档。请参考《MFC Internals》获得更详细的信息。 
    详细说明参见《深入浅出MFC》Q:文档何时被析构? A:在SDI程序中,程序退出后文档就被删除。在MDI程序中,与该文档相关的最后一个视图关闭时文档就被删除。为了在SDI和MDI中同时用这个文档,你应该在虚函数DeleteContents()函数中删除该文档的数据,而不是在析构器中。
      

  25.   

    Q:如何把一个项目变成 Unicode 版的:A:1、把"Setting" "C/C++" "Preprocessor definitions" 中的_MBCS 改为 _UNICODE。
        2、把"Setting" "Link" 中 Category 选为 "Output", 在 Entry-point symbol 中输入"wWinMainCRTStartup"。
        
    Q:How to Add Horizontal Scrolling to a List Box
    A normal Windows list box does not  implement a default horizontal scrolling. In order to do that, the horizontal extent must be modified: 
    // Add string to the ListBox
    m_listBox.AddString(m_sAddString); // Adjust Horizontal Scrolling Extent (if necessary)
    CClientDC dc(m_listBox);
    CSize size = dc.GetTextExtent(m_sAddString, m_sAddString.GetLength()); if (size.cx > m_listBox.GetHorizontalExtent())
    m_listBox.SetHorizontalExtent(size.cx);A sample of an Horizontal Scrolling List Box in an MFC Class can be found in the Microsoft Knowledge Base (Article ID: Q146437) Q: I'm trying to call a Windows API, but the compiler gives an undeclared identifier error (C2065). Why? (top)A:The most likely reason is that the header files you are using to build are out-of-date and do not include the features you're trying to use. The headers that came with Visual C++ 6 are extremely old, and if you are still using them, you will run into this problem often. You can get updated header files by downloading the Platform SDK from Microsoft. Microsoft recently created an online installer, much like Windows Update, called SDK Update. If you do not want to use this page (it requires you to install an ActiveX control), you can download the SDK CAB files and install the SDK from that local copy. You can also order the SDK on CD from the SDK Update site.If you have downloaded the latest header files and are still getting compiler errors, read on.The Windows header files can be used to build programs for any version of Windows starting with Windows 95 and NT 3.51. Since not all APIs are present in all versions of Windows, there is a system that prevents you from using APIs that aren't available in the Windows version you are targeting.This system uses preprocessor symbols to selectively include API prototypes. The symbols are:WINVER: Windows version (applies to 9x/Me and NT) 
    _WIN32_WINDOWS: Windows 9x/Me version 
    _WIN32_WINNT: Windows NT version 
    _WIN32_IE: Common controls version 
    By default, you can only use functions in Windows 95, NT 3.51, and pre-IE3 common controls. To use APIs introduced in later versions of Windows or the common controls, you need to #define the above symbols correctly before including any Windows headers.Q: I'm trying to call a Windows API, but the linker gives an unresolved external error (LNK2001) on the API name. Why? (top)A:When you call a function whose code is not in your program itself, such as any Windows API, you need to tell the linker where the function is so it can store information about the function in your EXE. This is done with an import library. An import library is a LIB file that contains the list of functions exported from its corresponding DLL. For example, kernel32.lib contains the exports for kernel32.dll. When Windows loads your EXE, it reads this information, loads the correct DLL, and resolves the function calls.The VC AppWizard creates projects with the most commonly-used LIBs (such as kernel32.lib, user32.lib, etc.) already listed in the linker options, but if you use call APIs in other DLLs, you'll need to add the corresponding LIB files.Let's take for example the API PathAddBackslash(). When you get an unresolved external error on this API, you need to find out which LIB file its definition is contained in. Read the MSDN page on PathAddBackslash(), and at the bottom you'll see: "Import Library: Shlwapi.lib". That tells you that you must add shlwapi.lib to your linker options.To add import libraries to the linker options, click Project->Settings and go to the Link tab. Set the Category combo box to General, then add the LIB filenames in the Object/library modules edit box.Q:Why do I get an unresolved external error (LNK2001) on main() when I make a release build of my ATL project? (top)A:Release builds of ATL projects contain an optimization whereby the project does not link with the C runtime library (CRT) in order to reduce the size of your binary. If you use any functions from the CRT (for example, string manipulation functions) or classes from the C++ library, you need to link with the CRT.In your project options, go to the C/C++ tab and select the Preprocessor category. Remove the symbol _ATL_MIN_CRT from the preprocessor defines, which will turn off this optimization.Search MSDN for "lnk2001 atl" and see KB article Q166480 (question #4) for more details.Q:Why do I get an unresolved external (LNK2001) error on _beginthreadex and _endthreadex? (top)A:This happens when you compile a project that uses MFC, but your compiler settings are set to use the single-threaded version of the C runtime library (CRT). Since MFC uses threads, it requires the multithreaded CRT. Since the single-threaded CRT doesn't contain _beginthreadex() and _endthreadex(), you get a linker error on those two functions.To change your CRT setting, click Project->Settings and go to the C/C++ tab. Set the Category combo box to Code Generation. In the Use run-time library combo box, chose one of the multithreaded versions of the CRT. For debug builds, choose Debug Multithreaded or Debug Multithreaded DLL. For release builds, choose Multithreaded or Multithreaded DLL. The versions that say "DLL" use MSVCRT.DLL, while the others do not depend on that DLL.Q: I have a dialog that does some lengthy processing, and I need to have a Cancel button so the user can abort the processing. How do I get the Cancel button to work? (top)A:First off, the reason the UI doesn't respond to the mouse is that your program is single-threaded, which means it is not pumping messages in the dialog's message queue while the thread is busy doing the processing.You have two choices, either move the processing to a worker thread, or keep the dialog single-threaded and periodically poll your message queue during your processing. Multithreading is beyond the scope of this FAQ, but see the Threads, Processes & Inter-Process Communication section of CodeProject for more info. As to the second solution, here is MFC code that will pump any messages waiting in your message queue:void ProcessMessages()
    {
    CWinApp* pApp = AfxGetApp();
    MSG msg;  while ( PeekMessage ( &msg, NULL, 0, 0, PM_NOREMOVE ))
          pApp->PumpMessage();
    }
    Call ProcessMessages() periodically in the code that does the lengthy processing. Q:How do I keep a window on top of all other windows? (top)A:To make your window topmost:// MFC:
      wndYourWindow.SetWindowPos ( &wndTopMost, 0, 0, 0, 0, 0, 
                                   SWP_NOMOVE|SWP_NOSIZE );
     
    // Win32 API:
      SetWindowPos ( hwndYourWindow, HWND_TOPMOST, 0, 0, 0, 0,
                     SWP_NOMOVE|SWP_NOSIZE );
    To revert back to normal:// MFC:
      wndYourWindow.SetWindowPos ( &wndNoTopMost, 0, 0, 0, 0, 
                                   SWP_NOMOVE|SWP_NOSIZE );
     
    // Win32 API:
      SetWindowPos ( hwndYourWindow, HWND_NOTOPMOST, 0, 0, 0, 0,
                     SWP_NOMOVE|SWP_NOSIZE );
    Q: How do I highlight an entire row of a list view control in report mode? (top)A:The full-row-select feature is enabled by setting an extended style of the list control.// MFC:
      wndYourList.SetExtendedStyle ( LVS_EX_FULLROWSELECT );// Win32 API:
      ListView_SetExtendedListViewStyle ( hwndYourList, LVS_EX_FULLROWSELECT );
      

  26.   

    谢谢楼上各位的支持,凡做过经验总结的都可以重新开零分贴贴出文章内容获取可用分和信誉分,贴了的话请麻烦把贴子url告诉我
      

  27.   

    CListCtrl 几点经验:http://expert.csdn.net/Expert/topic/2417/2417270.xml?temp=.200268
      

  28.   

    CListCtrl 几点经验 zhucde(【风间苍月】)
    部分转载)
    每个List控件都有一个CHeaderCtrl。且它的ID是0。
    CHeaderCtrl* pHeader =(CHeaderCtrl*)m_listCtrl.GetDlgItem(0);
    即使List控件非report模式,Header控件也存在,只是此时它的尺寸为0。
    可利用以下代码使得控件的第一列自适应大小:
    m_listctrl.SetColumnWidth( 0, LVSCW_AUTOSIZE );
    List 控件中的图标初始化时可如下设置:
    m_listCtrl.InsertItem( LVIF_TEXT | LVIF_IMAGE, nRow, sItemText, 0, 0, nImage
    , NULL);
    在运行中需动态改变可调用SetItem()函数.
    m_listCtrl.SetItem( 0, 0, LVIF_IMAGE, NULL, nImage, 0, 0, 0 );
    删去图标可将nImage设置为-1。以下代码可将列表框的第一列限定大小:
    BOOL CMyListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    {
            HD_NOTIFY   *pHDN = (HD_NOTIFY*)lParam;
            if((pHDN->hdr.code == HDN_BEGINTRACKW || pHDN->hdr.code == HDN_BEGIN
    TRACKA)
                    && pHDN->iItem == 0)            // Prevent only first (col# 
    0) from resizing
            {
                    *pResult = TRUE;                // disable tracking
                    return TRUE;                    // Processed message
            }
            return CListCtrl::OnNotify(wParam, lParam, pResult);
    }
    选定cell
    CListCtrl::OnClick(...)
    {
        int column;
        CRect m_rect;
        //the function below is provided in CListCtrl inPlace editing
        int index = GetRowColumnIndex(point, &column);
        if(index == -1)return;
        int offset = 0;
        for(int i = 0; i < column; i++)
          offset += GetColumnWidth(i);
        //Get the rectangle of the label and the icon
        GetItemRect(index, &m_rect, LVIR_BOUNDS);
        m_rect.left += offset + 4;
        //Get the columnWidth of the selected column
        m_rect.right = m_rect.left + GetColumnWidth(column);
        Update(index);
        CClientDC dc(this);    //this is the pointer of the current view
        dc.DrawFocusRect(m_rect);
    }Select NonFirst cell:void CMyListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
    {
            CListCtrl::OnLButtonDown(nFlags, point);
            int index;
            point.x = 2;
            if( ( index = HitTest( point, NULL )) != -1 )
            {
                    SetItemState( index, LVIS_SELECTED | LVIS_FOCUSED ,
                                    LVIS_SELECTED | LVIS_FOCUSED);
            }
    }
    HitTestEx的代码如下:
    判断点击的是哪一列:
    // HitTestEx - Determine the row index and column index for a point
    // Returns - the row index or -1 if point is not over a row
    // point - point to be tested.
    // col  - to hold the column index
    int CMyListCtrl::HitTestEx(CPoint &point, int *col) const
    {
     int colnum = 0;
     int row = HitTest( point, NULL );
     if( col ) *col = 0;
     // Make sure that the ListView is in LVS_REPORT
     if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
      return row;
     // Get the top and bottom row visible
     row = GetTopIndex();
     int bottom = row + GetCountPerPage();
     if( bottom > GetItemCount() )
      bottom = GetItemCount();
     // Get the number of columns
     CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
     int nColumnCount = pHeader->GetItemCount();
     // Loop through the visible rows
     for( ;row <=bottom;row++)
     {
      // Get bounding rect of item and check whether point falls in it.
      CRect rect;
      GetItemRect( row, &rect, LVIR_BOUNDS );
      if( rect.PtInRect(point) )
      {
       // Now find the column
       for( colnum = 0; colnum < nColumnCount; colnum++ )
       {
        int colwidth = GetColumnWidth(colnum);
        if( point.x >= rect.left
         && point.x <= (rect.left + colwidth ) )
        {
         if( col ) *col = colnum;
         return row;
        }
        rect.left += colwidth;
       }
      }
     }
     return -1;
    }
    选中一定范围内的Item
    // SelItemRange - Selects/Deselect a range of items
    // Returns              - The number of new items selected
    // bSelect              - TRUE to select, FALSE to deselect
    // nFirstItem           - index of first item to select
    // nLastItem            - index of last item to select
    int CMyListCtrl::SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem)
    {
            // make sure nFirstItem and nLastItem are valid
            if( nFirstItem >= GetItemCount() || nLastItem >= GetItemCount() )
                    return 0;
            int nItemsSelected = 0;
            int nFlags = bSelect ? 0 : LVNI_SELECTED;
            int nItem = nFirstItem - 1;
            while( (nItem = GetNextItem( nItem, nFlags )) >=0
                            && nItem <= nLastItem )
            {
                    nItemsSelected++;
                    SetItemState(nItem, bSelect ? LVIS_SELECTED : 0, LVIS_SELECT
    ED );
            }
            return nItemsSelected;
    }
    SetHeaderBitmap:static int _gnCols = 5;
    static int _gnColSize[] =
    {
     18,21,22,18,380
    };
    static CString _gcsColLabel[] =
    {
     _T("x"),
     _T("x"),
     _T("x"),
     _T("x"),
     _T("Description:")
    };
    void CRightView::BuildColumns()
    {
     // Insert the columns into the list control
     LV_COLUMN lvCol;
     lvCol.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
     for (int i = 0; i < _gnCols; ++i)
     {
      lvCol.iSubItem = i;
      lvCol.pszText = (char*)(LPCTSTR)_gcsColLabel[i];
      lvCol.cx = _gnColSize[i];
      lvCol.fmt = LVCFMT_LEFT;
      m_ListCtrl->InsertColumn(i, &lvCol);
     }
     for (int x = 0; x < _gnCols-1; x++)
      SetHeaderBitmap(x, nHeaderBmps[x], HDF_STRING);
    }
    void CRightView::SetHeaderBitmap(int nCol, int nBitmap, DWORD dwRemove)
    {
     CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
     HD_ITEM hdi;
     hdi.mask = HDI_FORMAT;
     pHeader->GetItem (nCol, &hdi);
     hdi.mask = HDI_BITMAP | HDI_FORMAT;
     hdi.fmt  |= HDF_BITMAP;
     hdi.hbm = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
      MAKEINTRESOURCE(nBitmap),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
     if (dwRemove)
      hdi.fmt &= ~dwRemove;
     pHeader->SetItem (nCol, &hdi);
    }
      

  29.   

    续上:
    删除功能的实现(VCBASE)要实现删除功能,必须取得选中表项的数和表项总数,并且需要从后向前进行依次删除,其原因是每个表项被删除后,其后各表项的索引号均会发生递减变化,如果采取从前向后删除的方法,就会造成无法正常删除选中的表项,其功能代码如下:void CVCLISTDlg::OnDel() //删除按钮功能{ // TODO: Add your control notification handler code hereint i,iState;int nItemSelected=m_ListCtrl.GetSelectedCount();//所选表项数int nItemCount=m_ListCtrl.GetItemCount();//表项总数if(nItemSelected<1) return;for(i=nItemCount-1;i>=0;i--){iState=m_ListCtrl.GetItemState(i,LVIS_SELECTED);if(iState!=0) m_ListCtrl.DeleteItem(i);}}通过专用按钮来实现排序功能,如本文的排序按钮对应的功能代码如下:(VCBASE)void CVCLISTDlg::OnSort() { // TODO: Add your control notification handler code herem_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,0);
    }还有很多,以后再贴吧!^_^
      

  30.   

    我很菜,收录的一篇文章与大家分享:VC++中的文件操作
    各种关于文件的操作在程序设计中是十分常见,如果能对其各种操作都了如指掌,就可以根据实际情况找到最佳的解决方案,从而在较短的时间内编写出高效的代码,因而熟练的掌握文件操作是十分重要的。本文将对Visual C++中有关文件操作进行全面的介绍,并对在文件操作中经常遇到的一些疑难问题进行详细的分析。
      1.文件的查找 
      当对一个文件操作时,如果不知道该文件是否存在,就要首先进行查找。MFC中有一个专门用来进行文件查找的类CFileFind,使用它可以方便快捷地进行文件的查找。下面这段代码演示了这个类的最基本使用方法。 
      CString strFileTitle; 
      CFileFind finder; 
      BOOL bWorking = finder.FindFile("C:\\windows\\sysbkup\\*.cab"); 
      while(bWorking) 
      { 
      bWorking=finder.FindNextFile(); 
      strFileTitle=finder.GetFileTitle(); 
      } 
      2.文件的打开/保存对话框 
      让用户选择文件进行打开和存储操作时,就要用到文件打开/保存对话框。MFC的类CFileDialog用于实现这种功能。使用CFileDialog声明一个对象时,第一个BOOL型参数用于指定文件的打开或保存,当为TRUE时将构造一个文件打开对话框,为FALSE时构造一个文件保存对话框。 
      在构造CFileDialog对象时,如果在参数中指定了OFN_ALLOWMULTISELECT风格,则在此对话框中可以进行多选操作。此时要重点注意为此CFileDialog对象的m_ofn.lpstrFile分配一块内存,用于存储多选操作所返回的所有文件路径名,如果不进行分配或分配的内存过小就会导致操作失败。下面这段程序演示了文件打开对话框的使用方法。 
      CFileDialog mFileDlg(TRUE,NULL,NULL, 
      OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_ALLOWMULTISELECT, 
      "All Files (*.*)|*.*||",AfxGetMainWnd()); 
      CString str(" ",10000); 
      mFileDlg.m_ofn.lpstrFile=str.GetBuffer(10000); 
      str.ReleaseBuffer(); 
      POSITION mPos=mFileDlg.GetStartPosition(); 
      CString pathName(" ",128); 
      CFileStatus status; 
      while(mPos!=NULL) 
      { 
      pathName=mFileDlg.GetNextPathName(mPos); 
      CFile::GetStatus( pathName, status ); 
      } 
      3.文件的读写 
      文件的读写非常重要,下面将重点进行介绍。文件读写的最普通的方法是直接使用CFile进行,如文件的读写可以使用下面的方法: 
        //对文件进行读操作 
        char sRead[2]; 
        CFile mFile(_T("user.txt"),CFile::modeRead); 
        if(mFile.GetLength()<2) 
        return; 
        mFile.Read(sRead,2); 
        mFile.Close(); 
        //对文件进行写操作 
        CFile mFile(_T("user.txt "), CFile::modeWrite|CFile::modeCreate); 
        mFile.Write(sRead,2); 
        mFile.Flush(); 
        mFile.Close(); 
        虽然这种方法最为基本,但是它的使用繁琐,而且功能非常简单。我向你推荐的是使用CArchive,它的使用方法简单且功能十分强大。首先还是用CFile声明一个对象,然后用这个对象的指针做参数声明一个CArchive对象,你就可以非常方便地存储各种复杂的数据类型了。它的使用方法见下例。 
        //对文件进行写操作 
        CString strTemp; 
        CFile mFile; 
        mFile.Open("d:\\dd\\try.TRY",CFile::modeCreate|CFile::modeNoTruncate|CFile::modeWrite); 
        CArchive ar(&mFile,CArchive::store); 
        ar<<  ar.Close(); 
        mFile.Close(); 
        //对文件进行读操作 
        CFile mFile; 
        if(mFile.Open("d:\\dd\\try.TRY",CFile::modeRead)==0) 
        return; 
        CArchive ar(&mFile,CArchive::load); 
         ar>>strTemp; 
          ar.Close(); 
        mFile.Close(); 
        CArchive的 << 和>> 操作符用于简单数据类型的读写,对于CObject派生类的对象的存取要使用ReadObject()和WriteObject()。使用CArchive的ReadClass()和WriteClass()还可以进行类的读写,如: 
        //存储CAboutDlg类 
        ar.WriteClass(RUNTIME_CLASS(CAboutDlg)); 
        //读取CAboutDlg类 
        CRuntimeClass* mRunClass=ar.ReadClass(); 
        //使用CAboutDlg类 
       CObject* pObject=mRunClass->CreateObject(); 
         ((CDialog* )pObject)->DoModal(); 
        虽然VC提供的文档/视结构中的文档也可进行这些操作,但是不容易理解、使用和管理,因此虽然很多VC入门的书上花费大量篇幅讲述文档/视结构,但我建议你最好不要使用它的文档。关于如何进行文档/视的分离有很多书介绍,包括非常著名的《Visual C++ 技术内幕》。 
        如果你要进行的文件操作只是简单的读写整行的字符串,我建议你使用CStdioFile,用它来进行此类操作非常方便,如下例。 
        CStdioFile mFile; 
        CFileException mExcept; 
        mFile.Open( "d:\\temp\\aa.bat", CFile::modeWrite, &mExcept); 
        CString string="I am a string."; 
        mFile.WriteString(string); 
       mFile.Close(); 
       4.临时文件的使用 
      
      正规软件经常用到临时文件,你经常可以会看到C:\Windows\Temp目录下有大量的扩展名为tmp的文件,这些就是程序运行是建立的临时文件。临时文件的使用方法基本与常规文件一样,只是文件名应该调用函数GetTempFileName()获得。它的第一个参数是建立此临时文件的路径,第二个参数是建立临时文件名的前缀,第四个参数用于得到建立的临时文件名。得到此临时文件名以后,你就可以用它来建立并操作文件了,如: 
        char szTempPath[_MAX_PATH],szTempfile[_MAX_PATH]; 
        GetTempPath(_MAX_PATH, szTempPath); 
        GetTempFileName(szTempPath,_T ("my_"),0,szTempfile); 
        CFile m_tempFile(szTempfile,CFile:: modeCreate|CFile:: modeWrite); 
        char m_char='a'; 
        m_tempFile.Write(&m_char,2); 
        m_tempFile.Close(); 
        5.文件的复制、删除等 
        MFC中没有提供直接进行这些操作的功能,因而要使用SDK。SDK中的文件相关函数常用的有CopyFile()、CreateDirectory()、DeleteFile()、MoveFile()。它们的用法很简单,可参考MSDN。
    **************************************************************************************************
    ×××××××××××××××××××××××××××××××××××××××××××××××××
    **************************************************************************************************
    如何进行文件操作[1]显示对话框,取得文件名CString FilePathName;
    CFileDialog dlg(TRUE);///TRUE为OPEN对话框,FALSE为SAVE AS对话框
    if (dlg.DoModal() == IDOK)
       FilePathName=dlg.GetPathName();相关信息:CFileDialog 用于取文件名的几个成员函数:
    假如选择的文件是C:\WINDOWS\TEST.EXE
    则(1)GetPathName();取文件名全称,包括完整路径。取回C:\WINDOWS\TEST.EXE
    (2)GetFileTitle();取文件全名:TEST.EXE
    (3)GetFileName();取回TEST
    (4)GetFileExt();取扩展名EXE[2]打开文件
    CFile file("C:\HELLO.TXT",CFile::modeRead);//只读方式打开
    //CFile::modeRead可改为 CFile::modeWrite(只写),
    //CFile::modeReadWrite(读写),CFile::modeCreate(新建)
    例子:
    {
    CFile file;
    file.Open("C:\HELLO.TXT",CFile::modeCreate|Cfile::modeWrite);
    .
    .
    .
    }[3]移动文件指针
    file.Seek(100,CFile::begin);///从文件头开始往下移动100字节
    file.Seek(-50,CFile::end);///从文件末尾往上移动50字节
    file.Seek(-30,CFile::current);///从当前位置往上移动30字节
    file.SeekToBegin();///移到文件头
    file.SeekToEnd();///移到文件尾[4]读写文件
    读文件:
    char buffer[1000];
    file.Read(buffer,1000);
    写文件:
    CString string("自强不息");
    file.Write(string,8);[5]关闭文件
    file.Close();
      

  31.   

    授人以鱼,不如授人以渔我认为应该教给初学者学习方法,解决问题的能力,学会自我总结以上的faq在网上都能找到,我认为对初学者来说,学会查资料是最重要的,对一些简单的问题,通常可以通过查资料自己解决的,而不要盲目的提问。在这里,我们谈经验,我认为应该谈如何解决问题的经验,比如,当我遇到一个问题时。我是怎样思考的,后来又怎么解决的,最后总结一下。。这样,收获不仅仅是这一个问题,而是类似的问题,以及解决问题的能力。。经常在csdn上看到一些问某某窗口控件如何用之类的问题,其实查查msdn就解决了,解决问题的方法非常简单以上是一家之言。不知大家以为然否。。
      

  32.   

    回复人: seedwq(小白) ( ) 信誉:97  2003-11-02 21:14:00  得分:0 
     
     
      关注,等自己有了经验也来写写。搂住可以开个新贴把这个帖子里的精华转过去,锁定成不能回复只能编辑
     ———————————————————————————————————————————
     
    如果这个帖子太大的话,我会考虑这么做的
     回复人: xiaohyy(醉大饿极) ( )  
    ————————————————————————————————————————————
    其实好多东西还是要靠经验的,尽管MSDN可能无所不能,但它太庞大了,又是英文,有时候还是问别人快一些。
    并不是所有的东西都可以在短时间内自己掌握的,有些东西,没有掌握,并不代表自己不会这个方面。如果能有一些资料来借鉴,我想会起到事半功倍的效果
      

  33.   

    菜鸟说句菜话:
    一开始学习时,别人说要多上机,多编码,可编来编去总觉得所得甚少,现在看来还是应该在看完基本理论后编几个简单程序把MFC的结构,然后打住拿别人的源程序来看,考虑清楚,多看之后再下手编程,如果轻率地拿来就编,可能会事半功倍。
    总结一句:看完基础-》编码-》看源程序-》思考-》编码。
      

  34.   

    我是一个新手
    是编程爱好者!尤其VC++
    希望和有共同爱的朋友交个朋友!  
    哪位大哥带我!
    QQ:829168
      

  35.   

    俺也来提供一个关于调试多线程程序的方法:1、对一条特定的线程进行调试
       Visual C++调试器支持多线的情况,如果你在许多线程都调用的函数中
       设置一个断点,每当一个线程遭遇到此断点,调试器就会在线程之间循环切换
       有个方法可以避开这个问题,就是挂起所有线程,除了你感兴趣的那条(要调试
       的那条),在调试器中打开[Debug]菜单并选择[Thread]你就可以获得一个线程对
       话框,在这里你可以挂起所有的线程,把你要调试的线程留下来,单独调试他2、运转记录
       就是让程序的关键部分显示出其发生的活动的一种方法!
       就是在程序的关键部位(某一时刻你想知道他的状态)加上一些输出语句,比如
       printf   TRACE  等等,可以将信息输出到屏幕,也可以重定向到文件中,不要
       用一些控件比如ListBox,因为ListBox也是一个窗口,在他的内部有自己的消息
       循环,向ListBox中输出调试信息的时候,会引起不必要的线程切换(从当前
       线程切换到ListBox的消息循环)非常耗时!这对多线程这种对时间很敏感的程序
       来说有可能会影响运行的结果!
       在GUI程序中也是可以拥有console窗口的,由于console窗口由系统的设备驱动
       程序负责,即使你的程序当掉或在调试器中停止console窗口仍有反应!3、内存记号(Memory Trails)
       上面的两种方法执行的时间都稍长,有可能影响到程序运行的结果,Memory Trail
       属于一种比较低阶的技术!
       是用Memory Trail的方法:
       你必须产生一个足够大的全局缓冲区,以及指向该缓冲区的全局指针,例如:
       char gMemTrail[16384];
       char *pMemTrail = gMemTrail;
       当你想输出某些东西到屏幕上或文件中时,你就写个记号到MemTrail中
       例如:
       *pMemTrail++ = 'f';
       你的程序的每一个追踪点都应该写一个不同的记号,不论什么时候你想要,或是
       程序当掉之后,你可以利用调试器看看 memory trail中的内容!3、硬件调试寄存器
       只适用于Intel机器!
       这个俺就不多说了,基本上很少用!这就是俺的方法,还请各位指正!
      

  36.   

    我喜欢用STL 尽量减少自己new和delete的机会 代码最好能够写得清晰得不用注释
    我很少用VC的 占个小座位听大家讲课(尽量不占地方)
      

  37.   

    VC常见入门问题总结(二)
    不好意思,这次只先总结出两条:(
    1:为何我的下拉列表框找不到添加的内容?
        VC如果不安装补丁的话,下拉列表框刚刚从控件栏拖到设计的对话框中时,其下拉列表的高度很小,所以经常会出现看不到内容的情况,在对话框设计中,点下列表框右侧的三角,然后再把下拉列表拖大即可。
    2:为何一个位图在打印状态下于屏幕大小比较显得非常小?
        可以认为位图是由颜色点阵构成的,因此一般情况下,它只有横向纵向的像素数的概念,而没有横向纵向的宽度高度具体值的概念,因此,位图的显示大小是由显示位图的设备的分辨率决定的。显示器的像素点的大小比起打印机要大很多,所以会出现位图打印以及打印预览状态下非常小的问题。解决的方式使用StretchBlt函数拉伸位图,见下面的小程序:
    /*
    //打印或者在屏幕上画位图
    //pDC 打印机或者屏幕dc指针
    iLogPixelX
    iLogPixelY
    屏幕DC的GetDeviceCaps(LOGPIXELSX)值,其中
    iLogPixelX = DC.GetDeviceCaps ( LOGPIXELSX ) ;
    iLogPixelY = DC.GetDeviceCaps ( LOGPIXELSY ) ;
    iResBMPID BMP资源ID
    */
    void DrawBMP (CDC * pDC, int iLogPixelX,int iLogPixelY,int iResBMPID)
    {
    CDC MemDC; // 内存设备环境指针,在视的整个存在过程都将存在
    CBitmap Bitmap;  
    CRect Source, Dest; // 记录源位图尺寸和最终显示尺寸
    BITMAP bm;
    if(MemDC.GetSafeHdc ( ) == NULL)
    {
    Bitmap.LoadBitmap ( iResBMPID ) ;
    MemDC.CreateCompatibleDC ( pDC ) ;
    MemDC.SelectObject( &Bitmap ) ;
    Bitmap.GetObject ( sizeof ( bm ), &bm ) ;
    Source.top = 0 ;
    Source.left = 0 ;
    Source.right= bm.bmWidth ;
    Source.bottom = bm.bmHeight ;
    Dest  =  Source ;
    }
    pDC->DPtoLP (& Dest ) ;
    if ( pDC -> IsPrinting ( ) )
    {
    Dest.left = ( int ) ( Dest.left * ( (double)pDC->GetDeviceCaps ( LOGPIXELSX ) ) / iLogPixelX ) ;
    Dest.right = ( int ) ( Dest.right * ( ( double ) pDC -> GetDeviceCaps ( LOGPIXELSX ) ) / iLogPixelX ) ;
    Dest.top = ( int ) ( Dest.top * ( ( double ) pDC -> GetDeviceCaps ( LOGPIXELSY ) ) / iLogPixelY ) ;
    Dest.bottom =( int ) ( Dest.bottom * ( ( double ) pDC -> GetDeviceCaps ( LOGPIXELSY ) ) / iLogPixelY ) ;
    }
    pDC -> StretchBlt ( Dest.left , Dest.top, Dest.right, Dest.bottom,
    &MemDC, Source.left, Source.top, Source.right,Source.bottom, SRCCOPY ) ;
    Bitmap.DeleteObject( ) ;
    MemDC.DeleteDC( ) ;
    return ;
    }
      

  38.   

    支持楼主!!!我也将自己在实际编程中的经验写一下:
    1、我在用VC操作ACCESS数据库的时候,发现了一个现象,大家如果用过ACCESS的话,都知道ACCESS数据可以导出为其他格式的文件,但是在导出为TXT文件的时候,就容易丢失精度,当初是一个LONG类型的数据,导出后,被自动截断,只保留了两位精度,而我根据提取出来的数据画图,结果小小的误差,差点被蒙骗过去,呵呵2、还有就是在使用指针的时候要注意,判断指针是否是合法地址不能通过判断地址来断定,因为非法地址可能是0xccccc  或者是0x000000  
      

  39.   

    虚拟按键的总结:http://expert.csdn.net/Expert/topic/2425/2425502.xml?temp=5.321902E-02
      

  40.   

    虚拟按键的总结:符号常量  十六进制值 指定的鼠标或键盘按键
    VK_LBUTTON  01  鼠标左键
    VK_RBUTTON  02  鼠标右键
    VK_CANCEL  03  Control-break 过程
    VK_MBUTTON  04  鼠标中键
    VK_BACK  08  BACKSPACE 键
    VK_TAB   09  TAB 键
    VK_CLEAR  0C  CLEAR 键
    VK_RETURN  0D  ENTER 键
    VK_SHIFT  10  SHIFT 键
    VK_CONTROL  11  CTRL 键
    VK_MENU  12  ALT 键
    VK_PAUSE  13  PAUSE 键
    VK_CAPITAL  14  CAPS LOCK 键
    VK_ESCAPE  1B  ESC 键
    VK_SPACE  20  SPACEBAR
    VK_PRIOR  21  PAGE UP 键
    VK_NEXT  22  PAGE DOWN 键
    VK_END   23  END 键
    VK_HOME  24  HOME 键
    VK_LEFT  25  LEFT ARROW 键
    VK_UP   26  UP ARROW 键
    VK_RIGHT  27  RIGHT ARROW 键
    VK_DOWN  28  DOWN ARROW 键
    VK_SELECT  29  SELECT 键
    VK_EXECUTE  2B  EXECUTE 键
    VK_SNAPSHOT  2C  PRINT SCREEN键(用于Windows 3.0及以后版本)
    VK_INSERT  2D  INS 键
    VK_DELETE  2E  DEL 键
    VK_HELP  2F  HELP 键
    /////////////////////
    对于非小键盘上的数字键和字母键,直接在单引号中加入该键就行.
    比如:a键:'A' b键:'B'
         1键:'1' 2键:'2'
    小键盘上可见上面,1键:VK_NUMPAD1
    /////////////////////////////////
    VK_LWIN  5B  Left Windows 键 (Microsoft自然键盘)
    VK_RWIN  5C  Right Windows 键 (Microsoft自然键盘)
    VK_APPS  5D  Applications 键 (Microsoft自然键盘)
    VK_NUMPAD0  60  数字小键盘上的 0 键
    VK_NUMPAD1  61  数字小键盘上的 1 键
    VK_NUMPAD2  62  数字小键盘上的 2 键
    VK_NUMPAD3  63  数字小键盘上的 3 键
    VK_NUMPAD4  64  数字小键盘上的 4 键
    VK_NUMPAD5  65  数字小键盘上的 5 键
    VK_NUMPAD6  66  数字小键盘上的 6 键
    VK_NUMPAD7  67  数字小键盘上的 7 键
    VK_NUMPAD8  68  数字小键盘上的 8 键
    VK_NUMPAD9  69  数字小键盘上的 9 键
    VK_MULTIPLY  6A  Multiply 键
    VK_ADD   6B  Add 键
    VK_SEPARATOR  6C  Separator 键
    VK_SUBTRACT  6D  Subtract 键
    VK_DECIMAL  6E  Decimal 键
    VK_DIVIDE  6F  Divide 键
    VK_F1  70  F1 键
    VK_F2  71  F2 键
    VK_F3  72  F3 键
    VK_F4  73  F4 键
    VK_F5  74  F5 键
    VK_F6  75  F6 键
    VK_F7  76  F7 键
    VK_F8  77  F8 键
    VK_F9  78  F9 键
    VK_F10  79  F10 键
    VK_F11  7A  F11 键
    VK_F12  7B  F12 键
    VK_F13  7C  F13 键
    VK_F14  7D  F14 键
    VK_F15  7E  F15 键
    VK_F16  7F  F16 键
    VK_F17  80H  F17 键
    VK_F18  81H  F18 键
    VK_F19  82H  F19 键
    VK_F20  83H  F20 键
    VK_F21  84H  F21 键
    VK_F22  85H  F22 键
    VK_F23  86H  F23 键
    VK_F24  87H  F24 键
    VK_NUMLOCK  90  NUM LOCK 键
    VK_SCROLL  91  SCROLL LOCK 键
    VK_ATTN  F6  Attn 键
    VK_CRSEL  F7  CrSel 键
    VK_EXSEL  F8  ExSel 键
    VK_EREOF  F9  Erase EOF 键
    VK_PLAY  FA  Play 键
    VK_ZOOM  FB  Zoom 键
    VK_OEM_CLEAR  FE  Clear 键
      

  41.   

    zhucde(【风间苍月】) :
    举例说明这些键的用法:
    (一)响应单独的按键:
    先添加PreTranslateMessage()(响应WM_CHAR)也是同样的效果,因为本例只捕捉键盘)
    BOOL CMydilog::PreTranslateMessage(MSG* pMsg) 
    {
    // TODO: Add your specialized code here and/or call the base class
    if (pMsg->message == WM_KEYDOWN)
    {
                  if(pMsg->wParam=='M')//直接用上面的虚码代替就可以响应所指键
    MessageBox("hello");//如果按下M键弹出消息.比如想当按下小键盘1时
                                          //弹出就用VK_NUMPAD1代替'M'
             }
    return CDialog::PreTranslateMessage(pMsg);
    }(二)组合键的用法:(本例响应Ctrl+X键)
    BOOL CMydilog::PreTranslateMessage(MSG* pMsg) 
    {
    // TODO: Add your specialized code here and/or call the base class

    if (pMsg->message == WM_KEYDOWN)
    {
    switch (pMsg->wParam)
    {
    case VK_ESCAPE:
    SetFocus ();
    return TRUE;
    case 'X':
    if(::GetKeyState(VK_CONTROL) < 0)//如果是Shift+X这里就
                                                                 //改成VK_SHIFT
    MessageBox("hello");
    return TRUE;

    }
    }
    return CDialog::PreTranslateMessage(pMsg);
    }
      

  42.   

    我一直很想问一个关于用VC开发数据库应用系统(C/S模式)的问题,可苦于找不到版面(感觉用VC开发数据库应用系统的比较少),找不到志同道好的。今天我总算找到了,就是楼上楼下的各位朋友。如果真的要用VC开发一个数据库应用系统(C/S模式,数据库用SQLSERVER開發),應采用哪种方案好。是用MFC ODBC,还是ADO,还有那个DAO,这些方法有什么不同,哪个好,哪个是现在流行的?大家可以談談嗎?