Qisong's profileMephisPhotosBlogLists Tools Help

Blog


    C/MFC - 什么是MFC

    『 ۞рēn-Ρlāм ™ 』 C/MFC - 什么是MFC

    MFC,微软基础类(Microsoft Foundation Classes),实际上是微软提供的,用于在C++环境下编写应用程序的一个框架和引擎,VC++是WinOS下开发人员使用的专业C++ SDK(SDK,Standard SoftWare Develop Kit,专业软件开发平台),MFC就是挂在它之上的一个输助软件开发包,MFC作为与VC++血肉相连的部分(注意C++和VC++的区别:C++是一种程序设计语言,是一种大家都承认的软件编制的通用规范,而VC++只是一个编译器,或者说是一种编译器+源程序编辑器的IDE,WS,PlatForm,这跟Pascal和Dephi的关系一个道理,Pascal是Dephi的语言基础,Dephi使用Pascal规范来进行Win下应用程序的开发和编译,却不同于Basic语言和VB的关系,Basic语言在VB开发出来被应用的年代已经成了Basic语言的新规范,VB新加的Basic语言要素,如面对对象程序设计的要素,是一种性质上的飞跃,使VB既是一个IDE,又成长成一个新的程序设计语言),MFC同BC++集成的VCL一样是一个非外挂式的软件包,类库,只不过MFC类是微软为VC++专配的..

    MFC是Win API与C++的结合,API,即微软提供的WinOS下应用程序的编程语言接口,是一种软件编程的规范,但不是一种程序开发语言本身,可以允许用户使用各种各样的第三方(如我是一方,微软是一方,Borland就是第三方)的编程语言来进行对Win OS下应用程序的开发,使这些被开发出来的应用程序能在WinOS下运行,比如VB,VC++,Java,Dehpi编程语言函数本质上全部源于API,因此用它们开发出来的应用程序都能工作在WinOS的消息机制和绘图里,遵守WinOS作为一个操作系统的内部实现,这其实也是一种必要,微软如果不提供API,这个世上对Win编程的工作就不会存在,微软的产品就会迅速从时尚变成垃圾,上面说到MFC是微软对API函数的专用C++封装,这种结合一方面让用户使用微软的专业C++ SDK来进行Win下应用程序的开发变得容易,因为MFC是对API的封装,微软做了大量的工作,隐藏了好多内节程序开发人员在Win下用C++ & MFC编制软件时的大量内节,如应用程序实现消息的处理,设备环境绘图,这种结合是以方便为目的的,必定要付出一定代价(这是微软的一向作风),因此就造成了MFC对类封装中的一定程度的的冗余和迂回,但这是可以接受的..

    最后要明白MFC不只是一个功能单纯的界面开发系统,它提供的类绝大部分用来进行界面开发,关联一个窗口的动作,但它提供的类中有好多类不与一个窗口关联,即类的作用不是一个界面类,不实现对一个窗口对象的控制(如创建,销毁),而是一些在WinOS(用MFC编写的程序绝大部分都在WinOS中运行)中实现内部处理的类,如数据库的管理类等,学习中最应花费时间的是消息和设备环境,对C++和MFC的学习中最难的部分是指针,C++面向对像程序设计的其它部分,如数据类型,流程控制都不难,建议学习数据结构C++版..

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/MFC - MFC应用程序脱离运行时刻库独立运行

    『 ۞рēn-Ρlāм ™ 』 C/MFC - MFC应用程序脱离运行时刻库独立运行

    如果使用Shared DLL方式,则生成的EXE非常小,但必须分发MFC42.DLL和MSVCRT.DLL。也可以设置使用Static Library,这时不用分发这两个DLL,但是得到的EXE比较大。

    另外,用AppWizard生成的工程里包含了两个设置:Win32 Debug和Win32 Release。而缺省激活的工程设置是Win32 Debug,用于调试。用这个设置编译出来的执行文件由于包含大量的调试信息,所以一般都要2M左右。真正发行的时候需要切换到Release模式下去重新生成。切换设置方式可以选择菜单Build|Set Active Configure...。会出来个对话框,在框中选Win32 Release模式,然后再重新编译。这样在工作目录下多出了Release目录,可发行的EXE也在这个目录里。一般Release模式下,静态连接的程序<600K。

    但是如果程序中使用了OLE或ActiveX技术,你必须分发olepro32.dll。如果编写的是数据库程序,必须分发相关的数据库DLL。

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/MFC - 编写自己的IDE

    『 ۞рēn-Ρlāм ™ 』 C/MFC - 编写自己的IDE

    何在图形界面中实时捕获控制台程序的标准输出
    本文未曾在商业媒体发表过, 如需转载, 请注明作者 [王咏刚] 和出处 [www.contextfree.net]
    IDE是集成开发环境(Integrated Development Environment)的简称。印象里有很多出色的IDE,比如JBuilder和Kylix,比如Visual Studio。不知大家是否留意过,大多数IDE本身只提供代码编辑、工程管理等人机交互功能,我们在IDE中编译代码、调试程序时,IDE需要调用命令行的编译器、调试器完成相应的操作。例如,使用Visual Studio编译C++程序时,我们会在IDE下方的Output窗口中看到编译和连接的全过程,虽然我们看不到弹出的DOS窗口,但实际上是IDE先后启动了Microsoft C++编译器cl.exe和连接器link.exe这两个命令行程序,而cl.exe和link.exe的输出又实时反映到了IDE的Output窗口中。还有,我们可以在Visual Studio中配置自己需要的工具程序(比如特殊的编译器),然后让Visual Studio在适当的时候运行这些工具,并将工具程序的输出实时显示到Output窗口中。下图是我在Visual Studio 6.0的Output窗口中运行J2SDK的javac.exe编译java源程序并显示程序中语法错误的情形:
     
    也就是说,大多数IDE工具都可以在集成环境中调用特定的命令行程序(WIN32里更确切的说法是控制台程序),然后实时捕获它们的输出(这多半是输出到标准的stdout和stderr流里的东西),并将捕获到的信息显示在图形界面的窗口中。
    这显然是一种具备潜在价值的功能。利用这一技术,我们至少可以
    1. 编写出自己的IDE,如果我们有足够的耐心的话;
    2. 在我们自己的应用程序里嵌入全文检索功能(调用Borland C++里的grep.exe工具),或者压缩和解压缩功能(调用控制台方式的压缩解压程序,比如arj.exe、pkzip.exe等);
    3. 连接其他人编写的,或者我们自己很久以前编写的控制台程序——我经常因为难以调用一个功能强大但又没有源码的控制台程序而苦恼万分。
    这样好的功能是如何实现的呢?
    首先,如果我们想做的是用一个控制台程序调用另一个控制台程序,那就再简单不过了。我们只消把父进程的stdout重定向到某个匿名管道的WRITE端,然后启动子进程,这时,子进程的stdout因为继承的关系也连在了管道的WRITE端,子进程的所有标准输出都写入了管道,父进程则在管道的另一端随时“侦听”——这一技术叫做输入输出的重定向。
    可现在的问题是,GUI方式的Windows程序根本没有控制台,没有stdin、stdout之类的东西,子进程又是别人写好的东西无法更改,这重定向该从何谈起呢?
    还有另外一招:我们可以直接在调用子进程时用命令行中的管道指令“>”将子进程的标准输出重定向到一个文件,子进程运行完毕后再去读取文件内容。这种方法当然可行,但它的问题是,我们很难实时监控子进程的输出,如果子进程不是随时刷新stdout的话,那我们只能等一整块数据实际写入文件之后才能看到运行结果;况且,访问磁盘文件的开销也远比内存中的管道操作来得大。
    我这里给出的方案其实很简单:既然控制台程序可以调用另一个控制台程序并完成输入输出的重定向,那我们完全可以编写一个中介程序,这个中介程序调用我们需要调用的工具程序并随时获取该程序的输出信息,然后直接将信息用约定的进程间通讯方式(比如匿名管道)传回GUI程序,就象下图中这样:
     
    图中,工具程序和中介程序都是以隐藏的方式运行的。工具程序原本输出到stdout的信息被重定向到中介程序开辟的管道中,中介程序再利用GUI程序创建的管道将信息即时传递到GUI程序的一个后台线程里,后台线程负责刷新GUI程序的用户界面(使用后台线程的原因是,只有这样才可以保证信息在GUI界面中随时输出时不影响用户正在进行的其他操作,就象我们在Visual Studio中执行耗时较长的编译功能那样)。
    我写的中介程序名字叫wSpawn,这个名字来自Visual Studio里完成类似功能的中介程序VcSpawn(你可以在Visual Studio的安装目录中找到它)。我的wSpawn非常简单,它利用系统调用_popen()同时完成创建子进程和输入输出重定向两件工作。GUI程序则使用一种特殊的命令行方式调用wSpawn:
    wspawn –h <n> <command> [arg1] [arg2] ...
    其中,-h后跟的是GUI程序提供的管道句柄,由GUI程序自动将其转换为十进制数字,wSpawn运行时将信息写入该句柄中,随后的内容是GUI程序真正要执行的命令行,例如调用C++编译器cl.exe的方式大致如下:
    wspawn –h 1903 cl /Id:\myInclude Test.cpp
    wspawn.cpp的程序清单如下:
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <string>
    #include <windows.h>

    using namespace std;

    void exit_friendly(void)
    {
        puts("请不要单独运行wSpawn.");
        exit(0);
    }

    int main( int argc, char *argv[] )
    {
        HANDLE  hWrite = NULL;
        DWORD   dwWrited;
        int     i = 0, ret = 0, len = 0;
        char    psBuffer[256];
        FILE*   child_output;
        string  command_line = "";

        // 检查命令行,如存在管道句柄,则将其转换为HANDLE类型
        if (argc < 2)
            exit_friendly();
        if (!stricmp(argv[1], "-h"))
        {
            if (argc < 4)
                exit_friendly();
            hWrite = (HANDLE)atoi(argv[2]);
            i = 3;
        }
        else
            i = 1;

        // 提取要执行的命令
        for (; i < argc; i++)
        {
            command_line += argv[i];
            command_line += " ";
        }

        // 使用_popen创建子进程并重定向其标准输出到文件指针中
        if( (child_output = _popen( command_line.c_str(), "rt" )) == NULL )
            exit( 1 );

        while( !feof( child_output ) )
        {
            if( fgets( psBuffer, 255, child_output ) != NULL )
            {
                if (hWrite)
                {
                    // 将子进程的标准输出写入管道,提供给自己的父进程
                    // 格式是先写数据块长度(0表示结束),再写数据块内容
                    len = strlen(psBuffer);
                    WriteFile(hWrite, &len, sizeof(int), &dwWrited, NULL);
                    WriteFile(hWrite, psBuffer, len, &dwWrited, NULL);
                }
                else
                    // 如命令行未提供管道句柄,则直接打印输出
                    printf(psBuffer);
            }
        }

        // 写“0”表示所有数据都已写完
        len = 0;
        if (hWrite)
            WriteFile(hWrite, &len, sizeof(int), &dwWrited, NULL);
        return _pclose( child_output );
    }
    下面,我们就利用wSpawn程序,写一个简单的“IDE”工具。我们选择Visual Studio 6.0作为开发环境(本文给出的代码也在Visual Studio.NET 7.0中做过测试)。首先,创建Visual C++工程myIDE,工程类型为MFC AppWizard(EXE)中的Dialog based类型,即创建了一个主窗口为对话框的GUI程序。工程myIDE的主对话框类是CMyIDEDlg。现在我们要在资源编辑器中为主对话框添加一个足够大的多行编辑框(Edit Box),它的控制ID是IDC_EDIT1,必须为IDC_EDIT1设置以下属性:
    Multiline, Horizontal scroll, Auto HScroll,
    Vertical scroll, Auto VScroll, Want return
    然后用ClassWizard为IDC_EDIT1添加一个对应的成员变量(注意变量的类型要选CEdit型而非字符串CString型)
    CEdit m_edit1;
    使用ClassWizard为“确定”按钮添加消息响应方法OnOK(),编辑该方法:
    void CMyIDEDlg::OnOK()
    {
        AfxBeginThread(myThread, this);
        InvalidateRect(NULL);
        UpdateWindow();
    }
    也就是说,我们在“确定”按钮按下时,启动了后台线程myThread(),那么,myThread()到底做了些什么呢?我们先在CMyIDEDlg类的头文件myIDEDlg.h中加上一个成员函数声明:
    protected:
        static UINT myThread(LPVOID pParam);
    然后,在CMyIDEDlg类的实现文件myIDEDlg.cpp里添加myThread()的实现代码:
    UINT CMyIDEDlg::myThread(LPVOID pParam)
    {
        PROCESS_INFORMATION pi;
        STARTUPINFO siStartInfo;
        SECURITY_ATTRIBUTES saAttr;
        CString Output, tmp;
        char command_line[200];
        DWORD dwRead;
        char* buf; int len;
        HANDLE hRead, hWrite;

        CMyIDEDlg* pDlg = (CMyIDEDlg*)pParam;

        // 创建与wSpawn.exe通讯的可继承的匿名管道
        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
        saAttr.bInheritHandle = TRUE;
        saAttr.lpSecurityDescriptor = NULL;
        if (!CreatePipe(&hRead, &hWrite, &saAttr, 0))
        {
            AfxMessageBox("创建管道失败");
            return 0;
        }

        // 准备wSpawn的命令行,在命令行给出写管道句柄和要wSpawn执行的命令
        memset(&pi, 0, sizeof(pi));
        sprintf(command_line, "wspawn -h %d cl /?", (unsigned int)hWrite);

        // 子进程以隐藏方式运行
        ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
        siStartInfo.cb = sizeof(STARTUPINFO);
        siStartInfo.wShowWindow = SW_HIDE;
        siStartInfo.dwFlags = STARTF_USESHOWWINDOW;

        // 创建wSpawn子进程
        if (!CreateProcess( NULL, command_line, NULL, NULL, TRUE,
                            0, NULL, NULL, &siStartInfo, &pi))
        {
            AfxMessageBox("调用wSpawn时失败");
            return 0;
        }

        // 读管道,并显示wSpawn从管道中返回的输出信息
        if(!ReadFile( hRead, &len, sizeof(int), &dwRead, NULL) || dwRead == 0)
            return 0;

        while(len)
        {
            buf = new char[len + 1];
            memset(buf, 0, len + 1);
            if(!ReadFile( hRead, buf, len, &dwRead, NULL) || dwRead == 0)
                return 0;

            // 将返回信息中的"\n"替换为Edit Box可识别的"\r\n"
            tmp = buf;
            tmp.Replace("\n", "\r\n");
            Output += tmp;

            // 将结果显示在Edit Box中,并刷新对话框
            pDlg->m_edit1.SetWindowText(Output);
            pDlg->InvalidateRect(NULL);
            pDlg->UpdateWindow();

            delete[] buf;
            if(!ReadFile( hRead, &len, sizeof(int), &dwRead, NULL) || dwRead == 0)
                return 0;
        }

        // 等待wSpawn结束
        WaitForSingleObject(pi.hProcess, 30000);
        // 关闭管道句柄
        CloseHandle(hRead);
        CloseHandle(hWrite);
        return 0;
    }
    很简单,不是吗?后台线程创建一个匿名管道,然后以隐藏方式启动wSpawn.exe并将管道句柄通过命令行传给wSpawn.exe,接下来只要从管道里读取信息就可以了。现在我们可以试着编译运行myIDE.exe了,记住要把myIDE.exe和wSpawn.exe放在同一目录下。还有,我在myThread()函数中写死了传给wSpawn.exe的待执行的命令行是“cl /?”,这模拟了一次典型的编译过程,如果你不打算改变这一行代码的话,那一定要注意在你的计算机上,C++编译器cl.exe必须位于环境变量PATH指明的路径里,否则wSpawn.exe可就找不到cl.exe了。下面是myIDE程序的运行结果:
     
    补充一点,上面给出的wSpawn利用_popen()完成子进程创建和输入输出重定向,这一方法虽然简单,但只能重定向子进程的stdout或stdin,如果还需要重定向子进程的stderr的话(Java编译器javac就利用stderr输出结果信息),那我们就不能这么投机取巧了。根据以上讨论,你一定可以使用传统的_pipe()、_dup()等系统调用,写出功能更完整的新版wSpawn来,我这里就不再罗嗦了。
    示例代码下载
    点击此处可下载本文的示例代码(ZIP格式,28KB)。
     
    [王咏刚,2002年5月]
     
     
    补充:相反方向的信息传递
    上面这篇文章在网上发布后,引起了一些反响。很多网友来信询问这样一个问题:上文中演示的是图形界面程序实时捕获控制台程序的输出;但有不少控制台程序是交互式运行的(如ftp客户端程序),需要人们在控制界面输入特定的指令才能完成相应的功能——能不能用类似的办法,让图形界面程序向控制台程序输入特定的命令行指令呢?
    如果我们想输入到控制台程序的指令序列是固定的,那完全可以使用更简单的办法:把命令序列存储在一个文本文件中,然后使用下面这样的重定向指令运行控制台程序:
    foo.exe < commands.txt
    但如果想输入到控制台的指令序列是由用户在操作图形界面程序时决定的,或是根据控制台程序的输出来决定的,我们就需要使用与上面文章中类似的管道法解决问题了。这一思路基本上和上文相同,只不过信息的传递方向颠倒了过来:图形界面程序在需要时将指令序列作为字符串传递给中介程序,中介程序将该字符串写入控制台程序的标准输入。
    实现这种相反功能的中介程序proxy的代码如下:
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <string>
    #include <windows.h>

    using namespace std;

    void exit_friendly(void)
    {
        puts("请不要单独运行proxy.");
        exit(0);
    }

    int main( int argc, char *argv[] )
    {
        HANDLE  hRead = NULL;
        DWORD   dwReaded;
        int     i = 0, ret = 0, len = 0;
        const int BUFFER_LEN = 256;
        char    psBuffer[BUFFER_LEN];
        FILE*   child_input;
        string  command_line = "";

        // 检查命令行,如存在管道句柄,则将其转换为HANDLE类型
        if (argc < 2)
            exit_friendly();
        if (!stricmp(argv[1], "-h"))
        {
            if (argc < 4)
                exit_friendly();
            hRead = (HANDLE)atoi(argv[2]);
            i = 3;
        }
        else
            i = 1;

        // 提取要执行的命令
        for (; i < argc; i++)
        {
            command_line += argv[i];
            command_line += " ";
        }

        // 使用_popen创建子进程并重定向其标准输入
        if( (child_input = _popen( command_line.c_str(), "wt" )) == NULL )
            exit( 1 );

        if (hRead)
        {
            while(1)
            {
                memset(psBuffer, 0, BUFFER_LEN);
                if (ReadFile(hRead, psBuffer, BUFFER_LEN, &dwReaded, NULL)
                        && dwReaded > 0)
                {
                    fputs(psBuffer, child_input);
                    fflush(child_input);

                    psBuffer[4] = 0;
                    if (!stricmp(psBuffer, "quit"))
                        break;
                }
            }
        }
        return _pclose( child_input );
    }
    图形界面程序中,创建管道并启动中介程序的示例代码如下:
        HANDLE hRead, hWrite;
        HANDLE hProcess;

        PROCESS_INFORMATION pi;
        STARTUPINFO siStartInfo;
        SECURITY_ATTRIBUTES saAttr;
        CString Output, tmp;
        char command_line[200];

        // 创建与proxy.exe通讯的可继承的匿名管道
        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
        saAttr.bInheritHandle = TRUE;
        saAttr.lpSecurityDescriptor = NULL;
        if (!CreatePipe(&hRead, &hWrite, &saAttr, 0))
        {
            AfxMessageBox("创建管道失败");
            EndDialog(IDCANCEL);
            return FALSE;
        }

        // 准备proxy.exe的命令行,在命令行给出写管道句柄和要proxy.exe执行的命令
        memset(&pi, 0, sizeof(pi));
        sprintf(command_line, "proxy -h %d ftp ...", (unsigned int)hRead);

        ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
        //siStartInfo.cb = sizeof(STARTUPINFO);
        //siStartInfo.wShowWindow = SW_HIDE;
        //siStartInfo.dwFlags = STARTF_USESHOWWINDOW;
        if (!CreateProcess( NULL, command_line, NULL, NULL, TRUE,
                            0, NULL, NULL, &siStartInfo, &pi))
        {
            AfxMessageBox("调用proxy.exe时失败");
            EndDialog(IDCANCEL);
            return FALSE;
        }
        hProcess = pi.hProcess;
    图形界面程序中,向控制台程序发送某特定命令的示例代码如下:
        char command = "help";
        DWORD dwWritten;
        WriteFile(hWrite, command, strlen(command), &dwWritten, NULL);
    显然,利用这两种方向的管道,我们很容易为一个纯控制台界面的程序加上一层图形用户界面的漂亮外壳。

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/MFC - GetCurrentDirectory

    『 ۞рēn-Ρlāм ™ 』 C/MFC - GetCurrentDirectory

    GetCurrentDirectory只是获取当前的目录,当计算机重新启动后,当前的路径一般为系统目录,因此系统启动后,使用该函数只能获取当前的目录,而不是应用程序所在的目录。我建议你使用GetModuleFileName函数,GetModuleFileName()是一个得到路径的API函数。本函数将这个API函数封装在其中,为的是简化调用的目的。

    当执行这个函数时,返回本程序所在的绝对路径,包括本程序的文件名。 具体使用方法为:

    //获取主程序所在路径,存在sPath中

    CString sPath;

    GetModuleFileName(NULL,sPath.GetBufferSetLength (MAX_PATH+1),MAX_PATH);

    sPath.ReleaseBuffer ();

    int nPos;

    nPos=sPath.ReverseFind (’’\\’’);

    sPath=sPath.Left (nPos);

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/MFC - 文件操作

    『 ۞рēn-Ρlāм ™ 』 C/MFC - 文件操作

    种关于文件的操作在程序设计中十分常见,如果能对这些操作都了如指掌,就可以根据实际情况找到最佳的解决方案,从而可以在较短的时间内编写出高效的代码。本文对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_OVER
    WRITEPROMPT|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<<strTemp;
    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.Read
    Class();
    //使用CAboutDlg类
    CObject* pObject=mRunClass->CreateObject();
    ((CDialog* )pObject)->DoModal(); 

    虽然VC提供的文档/视结构中的文档也可进行这些操作,但是不容易理解、使用和管理,如果要进行的文件操作只是简单的读写整行的字符串,建议使用“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。

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/MFC - 文本文件的简单操作

    『 ۞рēn-Ρlāм ™ 』 C/MFC - 文本文件的简单操作

    [ 作者: talkingmute ] 
    CFile的派生类CStdioFile提供了对文件进行流式的操作功能。其中函数void CStdioFile::WriteString( LPCTSTR lpsz )写入一个字符串,需要给字符串lpsz的末尾加上换行标志”\r\n”;函数bool CStdioFile::ReadString(CString &rString )从文件中读取一行,如果文件未读完返回true,否则返回false。

    比如:写入文件的例子

    //创建文件


    CStdioFile file;
    file.Open("ts.txt",CFile::modeCreate|CFile::modeWrite);

    //写入文件

    CString str;
    str.Format("%s\r\n","hello!I am talkingmute!");
    file.Seek(0,CFile::end);
    file.WriteString( str );

    //关闭文件

    file.Close();

    比如:读文件的例子

    CString strText = “”;
    CString szLine = “”;

    //打开文件
    CStdioFile file;
    file.Open("ts.txt",CFile::modeRead);

    //逐行读取字符串
    while( file.ReadString( szLine ) )
    {
     strText += szLine;
    }

    MessageBox(strText);

    //关闭文件

    file.Close();

     

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』