VC有关应用程序的平台版本区别的一点心得

  • 作者:rick1126
    email: rickzhang@sina.com
    日期:2001-3-14 16:57:00
    想必各位也看到了我在论坛上面发出的关于CRgn::GetRegionData的帖子, 最近从公司的一位高手那里很偶然的知道了原因. 就是在函数的头文件里面存在着对于不同OS的版本判定所进行的不同函数处理, 我的那个函数在强制设置为win200模式以后就正常了. 具体的介绍如下(翻译自MSDN的WINVER关键字:


    使用VC5.0/6.0自动产生的 .mak 文件是缺省针对 WinNT3.5的. 这样要使用NT4+以后的函数则受到了条件型代码的博阿户, 必须定义正确的应用程序宏进行设置. 否则, 你可能得到下列错误:
    error C2065: undeclared identifier.
    你也需要确保 INCLUDE 环境变量包含所需函数的SDK头文件路径位于VC的头文件路径之前. 否则你会在发布应用程序时得到 C2065 错误.

    下面是你需要针对不同系统所定义的宏:
    Windows 95 and Windows NT 4.0 WINVER=0x0400

    Windows 98 and Windows NT 4.0 _WIN32_WINDOWS=0x0410 and WINVER=0x0400

    Windows NT 4.0 _WIN32_WINNT=0x0400 and WINVER=0x0400

    Windows 98 and Windows 2000 WINVER=0x0500

    Windows 2000 _WIN32_WINNT=0x0500 and WINVER=0x0500

    Internet Explorer 3.0 _WIN32_IE=0x0300

    Internet Explorer 4.0 _WIN32_IE=0x0400

    Internet Explorer 5.0 _WIN32_IE=0x0500


    注意: 设置 WINVER 为 0x0500 可以缺省 _WIN32_IE=0x0400.

    如果你需要自己编写 .mak 文件, WIN32.MAK 内的宏有助于你进行正确地转换. _WIN32_WINNT 在 WIN32.MAK 基于你选择的操作系统而被设置. 缺省情况下, WIN32.MAK 定义 _WIN32_WINNT 等于 0x0400, _WIN32_IE 等于 0x0300. 如果需要使用 IE4+ 的新特性, 比如新增的通用控件等, 就需要重新设置 _WIN32_IE 等于 0x0400.

    如果你在你的 mak 文件里面没有包含 WIN32.MAK , 就需要额外定义 _WIN32_WINNT 等于 0x0500 得到 win2000的机制.

    SDK 头文件使用判断语句决定系统对应的支持函数. 下面的表格描述了这些语句.

    #if _WIN32_WINNT >= 0x0400 WinNT4+ , 在win95中不被实现

    #if _WIN32_WINDOWS >= 0x0410 Win98. 图象在Win95不保证显示正常

    #if _WIN32_WINNT >= 0x0500 Win2000. 图象在 Win9x 或者 NT 上不保证显示正常

    #if WINVER >= 0x0500 Win2000/Win98. 图象在Win95/NT上不保证显示正常

    #if _WIN32_IE >= 0x0300 IE3+
    #if _WIN32_IE >= 0x0400 IE4+
    #if _WIN32_IE >= 0x0500 IE5+

    对于 Win95se2 而言某些函数需要 (_WIN32_WINNT >= 0x0400) 进行判断, 诸如 Crypto API. 如果你针对 Win95se2进行应用编程, 而你需要使用这些函数的话就需要设置 _WIN32_WINNT 为 0x0400. 注意, 使用这些函数的应用程序在其他版本的 win95 上面可能运行不正常. 通常意义上, 应用程序缺省在win95上面运行就比如在编译时包含 _WIN32_WINNT 定义.

  • 作者:hxfwsk
    email: hxfwsk@hotmail.com
    日期:2001-6-18 14:55:40
    在RichEdit中如何将光标移动到指定的行?(转载)
    void __fastcall TRichEditTerm::SetCursor(int ARow, int ACol)
    {//ARow and ACol start from 0
    int i,j,iSelStart;
    AnsiString as;

    if(ARow<0)
    ARow=0;
    if(ACol<0)
    ACol=0;

    iSelStart=0;
    if(ARow>Lines->Count-1)
    {//Require add lines
    j=ARow-Lines->Count+1;
    for(i=1;i<=j;i++)
    Lines->Add("");
    for(i=0;i<ARow;i++)
    iSelStart+=Lines->Strings[i].Length()+2;
    i=Text.Length();
    }
    else if(ARow<=Lines->Count-1)
    {
    for(i=0;i<ARow;i++)
    iSelStart+=Lines->Strings[i].Length()+2;
    }

    j=Lines->Strings[ARow].Length();
    if(j<ACol)
    {
    as=Lines->Strings[ARow];
    for(i=0;i<ACol-j;i++)
    as.Insert(" ",j+1);
    Lines->Strings[ARow]=as;
    }
    SelStart=iSelStart+ACol;
    SendMessage(Handle,EM_SCROLLCARET,0,0);
    }
  • 作者:飞鸟
    email: flybird@chinaasp.com
    日期:00-7-3 下午 04:57:15
    在vb组件内调用excel2000实现GIF饼图
      当我第一次使用excel的时候,就为excel的图表功能所倾倒,实在强大,并且那些图也挺漂亮了。后来我尝试着在vb里面调用excel所支持的vba功能,发现功能的确强大,就是十分繁琐。后来就考虑用vb在excel外面包一层,写成对象,去掉我们不需要的特性。这样掉用起来就方便多了,所谓一劳永逸 :P。
      在这里,我将像大家介绍一个用vb编写的饼图组件,你只需要给它几个简单的参数,就可以生成一副GIF格式的图片给你。调用例子如下:

    Dim obj
    Set obj = CreateObject("ChinaaspChart.pie")
    obj.AddValue "男", 150
    obj.AddValue "女", 45
    obj.AddValue "不知道", 15
    obj.ChartName = "性别比例图"
    obj.FileName = "d:/123.gif"
    obj.SaveChart
      除了在vb里面可以调用,这段代码同样也可以在asp里面调用。

      下面请follow me 编写我们的组件。
       1.New project , 请选择activex dll,在project explorer面板上选择project1,然后在属性面板上修改其name为ChinaASPChart。同样把里面的class modules修改为pie

       2.保存该project,将project存为chinaaspchart.vbp,将class1.cls存为pie.cls。

       3.菜单project,选择菜单项References,然后请把Microsoft Active Server Pages Ojbect Library、Microsoft Excel 9.0 Object Library、COM+ Services Type Library选上。
    注意:在NT4/win98上没有COM+ Service Type Library这个东东,应该选Microsoft Transaction Server Type Library

       4.编辑pie.cls,代码如下:[QUOTE]
    ’-------------------------------------------------------------------------------
    Dim xl
    Dim m_chartName
    Dim m_chartData()
    Dim m_chartType
    Dim m_fileName
    Public ErrMsg
    Public foundErr
    Dim iCount
    Type m_Value
    label As String
    value As Double
    End Type
    Dim tValue As m_Value
    Public Property Let ChartType(ChartType)
    m_chartType = ChartType
    End Property
    Public Property Get ChartType()
    ChartType = m_chartType
    End Property

    Public Property Let ChartName(ChartName)
    m_chartName = ChartName
    End Property
    Public Property Get ChartName()
    ChartName = m_chartName
    End Property
    Public Property Let FileName(fname)
    m_fileName = fname
    End Property
    Public Property Get FileName()
    FileName = m_fileName
    End Property

    Public Sub AddValue(label, value)
    iCount = iCount + 1
    ReDim Preserve m_chartData(iCount)
    tValue.label = label
    tValue.value = value
    m_chartData(iCount) = tValue
    End Sub
    Public Sub SaveChart()
    On Error Resume Next
    Dim iSheet
    Dim i
    Set xl = New Excel.Application
    xl.Application.Workbooks.Add
    xl.Workbooks(1).Worksheets("sheet1").Activate
    [i] If Err.Number <> 0 Then
    foundErr = True
    ErrMsg = Err.Description
    Err.Clear
    Else
    xl.Workbooks(1).Worksheets("sheet1").Cells("2,1").value = m_chartName
    For i = 1 To iCount
    xl.Worksheets("Sheet1").Cells(1, i + 1).value = m_chartData(i).label
    xl.Worksheets("Sheet1").Cells(2, i + 1).value = m_chartData(i).value
    Next
    xl.Charts.Add
    xl.ActiveChart.ChartType = m_chartType
    xl.ActiveChart.SetSourceData xl.Sheets("Sheet1").Range("A1:" & Chr((iCount Mod 26) + Asc("A")) & "2"), 1
    xl.ActiveChart.Location 2, "Sheet1"
    With xl.ActiveChart
    .HasTitle = True
    .ChartTitle.Characters.Text = m_chartName
    End With
    xl.ActiveChart.ApplyDataLabels 2, False, _
    True, False
    With xl.Selection.Border
    .Weight = 2
    .LineStyle = 0
    End With

    xl.ActiveChart.PlotArea.Select
    With xl.Selection.Border
    .Weight = xlHairline
    .LineStyle = xlNone
    End With
    xl.Selection.Interior.ColorIndex = xlNone

    xl.ActiveWindow.Visible = False

    xl.DisplayAlerts = Fal
  • 作者:九流
    email: molimin@163.net
    日期:8/2/2001 12:33:52 PM
    引言
    钩子的本质是一段用以处理系统消息的程序,通过系统调用,把它挂入系统。钩子的种类很多,每种钩子可以截获并处理相应的消息,每当特定的消息发出,在到达目的窗口之前,钩子程序先行截获该消息、得到对此消息的控制权。此时钩子函数可以对截获的消息进行加工处理,甚至可以强制结束消息的传递。这有点类似与MFC中的PreTranslateMessage函数,所不同的是该函数只能用于拦截本进程中的消息,而对系统消息则无能为力。
    二、Win32系统钩子的实现
    每种类型的钩子均由系统来维护一个钩子链,最近安装的钩子位于链的开始,拥有最高的优先级,而最先安装的钩子则处在链的末尾。要实现Win32的系统钩子,首先要调用SDK中的API函数SetWindowsHookEx来安装这个钩子函数,其原型是:
    HHOOK SetWindowsHookEx(int idHook,
    HOOKPROC lpfn,
    HINSTANCE hMod,
    DWORD dwThreadId);
    其中,第一个参数是钩子的类型,常用的有WH_MOUSE、WH_KEYBOARD、WH_GETMESSAGE等;第二个参数是钩子函数的地址,当钩子钩到任何消息后便调用这个函数;第三个参数是钩子函数所在模块的句柄;第四个参数是钩子相关函数的ID用以指定想让钩子去钩哪个线程,为0时则拦截整个系统的消息此时为全局钩子。如果指定确定的线程,即为线程专用钩子。
    全局钩子函数必须包含在DLL(动态链接库)中,而线程专用钩子则可包含在可执行文件中。得到控制权的钩子函数在处理完消息后,可以调用另外一个SDK中的API函数CallNextHookEx来继续传递该消息。也可以通过直接返回TRUE来丢弃该消息,阻止该消息的传递。
    使用全局钩子函数时需要以DLL为载体,VC6中有三种形式的MFC DLL可供选择,即Regular statically linked to MFC DLL(标准静态链接MFC DLL)、Regular using the shared MFC DLL(标准动态链接MFC DLL)以及Extension MFC DLL(扩展MFC DLL)。第一种DLL在编译时把使用的MFC代码链接到DLL中,执行程序时不需要其他MFC动态链接类库的支持,但体积较大;第二种DLL在运行时动态链接到MFC类库,因而体积较小,但却依赖于MFC动态链接类库的支持;这两种DLL均可被MFC程序和Win32程序使用。第三种DLL的也是动态连接,但做为MFC类库的扩展,只能被MFC程序使用。
    三、Win32 DLL
    Win32 DLL的入口和出口函数都是DLLMain这同Win16 DLL是有区别的。只要有进程或线程载入和卸载DLL时,都会调用该函数,其原型是:
    BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved);其中,第一个参数表示DLL的实例句柄;第三个参数系统保留;第二个参数指明了当前调用该动态连接库的状态,它有四个可能的值:DLL_PROCESS_ATTACH(进程载入)、DLL_THREAD_ATTACH(线程载入)、DLL_THREAD_DETACH(线程卸载)、DLL_PROCESS_DETACH(进程卸载)。在DLLMain函数中可以通过对传递进来的这个参数的值进行判别,根据不同的参数值对DLL进行必要的初始化或清理工作。由于在Win32环境下,所有进程的空间都是相互独立的,这减少了应用程序间的相互影响,但大大增加了编程的难度。当进程在动态加载DLL时,系统自动把DLL地址映射到该进程的私有空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间,每个进程所拥有的相同的DLL的全局数据其值却并不一定是相同的。当DLL内存被映射到进程空间中,每个进程都有自己的全局内存拷贝,加载DLL的每一个新的进程都重新初始化这一内存区域,也就是说进程不能再共享DLL。因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。一种方法便是把这些需要共享的数据单独分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享,建立一个内存共享的DLL。

    四、全局共享数据的实现
    可以用#pragma data_seg建立一个新的数据段并定义共享数据,其具体格式为:
    #pragma data_seg ("shareddata")
    HWND sharedwnd=NULL;//共享数据
    #pragma data_seg()
    所有在data_seg pragmas语句之间声明的变量都将在shareddata段中。仅定义一个数据段还不能达到共享数据的目的,还要告诉编译器该段的属性,有两种方法可以实现该目的(其效果是相同的),一种方法是在.DEF文件中加入如下语句:
    SETCTIONS
    shareddata READ WRITE SHARED
    另一种方法是在项目设置链接选项中加入如下语句:
    /SECTION:shareddata,rws
    五、鼠标钩子程序示例
    本示例程序用到全局钩子函数,程序分两部分:可执行程序MouseDemo和动态连接库MouseHook。首先编制MFC扩展动态连接库MouseHook.dll:
    (一)选择MFC AppWizard(DLL)创建项目Mousehook;
    (二)选择MFC Extension DLL(MFC扩展DLL)类型;
    (三)通过Project菜单的AddToProject子菜单的"New…"添加头文件MouseHook.h。
    (四)在头文件中建立钩子类:
      class AFX_EXT_CLASS CMouseHook:public CObject
      {
      public:
      CMouseHook();  //钩子类的构造函数
      ~CMouseHook();  //钩子类的析构函数
      BOOL StartHoo
  • 作者:茶馆主人
    日期:2001-6-3 20:57:30
    在组件中非模式PropertySheet中出现Tab键失效的解决办法

    北京商即通数码科技有限公司 张宏

    有不少朋友在使用组件中使用非模式对话框或非模式属性表中出现Tab键、光标键及其他热键失效,TAB键不能够让光标在控件间移动光标,而在模式对话框中则正常。这种情况是不稳定的,有时候出现,有时候并不出现,真让人不解。
    分析这个问题,原因是ActiveX控件本身没有消息泵,而是由COM组件的客户端(主程序)拥有,因此,键盘输入消息被主程序拦截并未正常分发至COM组件的非模式对话框或者非模式PropertySheet。
    解决的办法是在自己派生的PropertySheet或者对话框类中安装一个Windows的WH_GETMESSAGE消息钩子,并且打开和处理键盘事件。
    代码如下:
    1.定义一个公共变量
    HHOOK hHook;
    2.写一个公共回调函数GetMsgProc处理键盘事件:
    // 用消息钩子解决非模式属性表中的Tab键及其他热键失效问题
    // 钩子处理器
    LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
    {

    // Switch the module state for the correct handle to be used.
    AFX_MANAGE_STATE(AfxGetStaticModuleState( ));

    // If this is a keystrokes message, translate it in controls’
    // PreTranslateMessage().
    LPMSG lpMsg = (LPMSG) lParam;
    if( (nCode >= 0) &&
    PM_REMOVE == wParam &&
    (lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST) &&
    AfxGetApp()->PreTranslateMessage((LPMSG)lParam) )
    {
    // The value returned from this hookproc is ignored, and it cannot
    // be used to tell Windows the message has been handled. To avoid
    // further processing, convert the message to WM_NULL before
    // returning.
    lpMsg->message = WM_NULL;
    lpMsg->lParam = 0L;
    lpMsg->wParam = 0;
    }



    // Passes the hook information to the next hook procedure in
    // the current hook chain.
    return ::CallNextHookEx(hHook, nCode, wParam, lParam);

    }
    3. 在OnInitDialog函数中安装钩子

    //安装对键盘事件的钩子
    hHook = ::SetWindowsHookEx(
    WH_GETMESSAGE,
    GetMsgProc,
    AfxGetInstanceHandle(),
    GetCurrentThreadId());
    ASSERT (hHook);
    4. 在关闭对话框时OnClose取消钩子

    //取消钩子
    VERIFY (::UnhookWindowsHookEx (hHook));

    现在,编译程序,问题解决,Tab键能够正常响应了。

  • 作者:三月
    email: ocean_sky002@263.net
    日期:7/26/2001 3:47:26 PM
    文章出处:赛迪网 作者:刘雨楠、谭章熹 


    用户在使用VC++开发应用程序时,主要有两种方法:调用Windows提供的API函数(Application Programming Interface)和直接使用Microsoft提供的MFC类库(Microsoft Foundation Class)。与MFC相比,API更贴近操作系统,也是每一个Windows程序员必须掌握的。

    本文将以一个类似Windows的计算器程序为例,简述Windows API程序设计的方法和一些基本技巧。

    思路和算法
    1、首先构造输入和输出界面
    首先构造输入和输出界面。在主窗口中,用CreateWindow( )函数来创建按钮和编辑框等子窗口控件。这样,用户可通过按钮进行输入,并通过编辑框显示计算结果。
    2、本程序通过表达式堆栈算法来支持科学计算功能
    以字符型数组express[ ]存放整个表达式,并定义void Push(char)和void Pop(void)来实现向数据堆栈OVS和算符堆栈OPS压入和弹出一个操作符或数据。
    3、为了进行正常的计算,必须保证表达式的合法性
    程序一方面要对用户的输入加上必要的过滤,例如不允许出现非法表达式,另一方面要对非法运算数据作判断。
    4、用户点击“=”完成所有的输入后,调用void CalculateOperation()函数进行计算
    具体实现方法如下:用指针i从express中扫描输入的表达式,若扫描到数值,则压入数据栈OVS,若扫描到运算符则判断其优先级是否大于或等于算符堆栈OPS栈顶的优先级,如果大于其优先级则压入OPS并扫描下一字符,反之则弹出OVS和OPS栈顶元素进行一次运算,并将结果压入OVS栈。反复执行此过程,直到完成表达式的计算。

    程序主体结构
    本程序以WinMain( )函数为入口,并依次进行窗口类的初始化和登记,以及窗口的创建和显示。在消息循环里,主函数反复调用WinProc( )回调函数(CallBack Function)以处理捕获的消息。因此全部消息的响应代码要写在WinProc( )中,这也是用Windows API编写程序最核心的部分。

    另外,用API设计程序的一大特点就是对任何元素操作都需要通过获得相应的句柄(Handle)来实现。

    程序功能及特色
    本程序除一般的科学计算功能外,还有一些扩展功能。

    1. 可动态更换背景图案
    类似Winamp播放器有各种各样的Skin,本程序也可动态更换背景。其核心思路就是在WM_ERASEBKGND消息响应时,不用系统缺省的白色画刷,而用资源中的位图来重画背景。为了提高重画效率和避免闪烁,开发时一定注意不要把背景重画代码放在WM_PAINT消息响应中。

    另外为使背景图案能跟随窗口尺寸变化,在显示位图时,采用StretchBlt函数代替BitBlt函数,这样在更换背景图案时,不论资源中的位图是否与当前窗口的大小相等,都会自动拉伸或收缩以适应窗口的尺寸。具体代码如下:

    case WM_ERASEBKGND:
    {
    RECT rc;
    hdc = (HDC)wParam;
    // wParam的值为当前窗口的hDC
    SelectObject(hdcmem,hbitmap);
    GetClientRect(hwnd,&&rc);
    //获得客户区域大小
    StretchBlt( hdc,0,0,rc.right,rc.bottom,hdcmem,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY );
    }
    break;
    其中,hdc是当前窗口的DC,hdcmem是与当前屏幕兼容的内存镜像设备句柄。

    为了使用方便,在选单中可创建背景子选单。资源文件的代码如下:

    // Menu
    MENU MENU DISCARDABLE
    BEGIN
    POPUP "设置(&&M)"
    BEGIN
    POPUP "背景(&&B)"
    BEGIN
    MENUITEM "GREEN", IDM_GREEN
    MENUITEM "SNOW", IDM_SNOW
    MENUITEM "BUTTERFLY", IDM_BUTTERFLY
    MENUITEM "FLOWER", IDM_FLOWER
    END
    END
    END
    // Bitmap
    SNOW BITMAP MOVEABLE PURE "snow.bmp"
    GREEN BITMAP MOVEABLE PURE "green.bmp"
    BUTTERFLY BITMAP MOVEABLE PURE "butterfly.bmp"
    FLOWER BITMAP MOVEABLE PURE "flower.bmp"
    其中,IDM_GREEN等是与选单项对应的ID(定义在头文件中),snow.bmp等是位图文件的文件名。

    2. 高级功能与一般功能的切换
    Windows中的“计算器”程序,可实现“科学型”与“标准型”的切换。同样,本程序也设有类似功能。在选单中,点击“统计计算”,窗口会向外拉出一块,显示与统计计算有关的几个新按钮,再点击一次,窗口又会恢复原状。

    为实现此功能,我们首先在响应WM_CREATE消息时,将几个“统计计算”按钮事先创建在窗口区域之外,使这几个按钮不可见,用户便不能通过这几个按钮进行输入。当“统计计算”的选单项被选中时,用SetWindowPos( )函数来扩大窗口边界,使这几个按钮变为可见,这样就等于增加了新的功能。具体代码如下:

    case IDM_STATISTIC:
    // IDM_STATISTIC是与“统计计算”选单项对应的ID
    {
    HMENU hmnu = GetMenu(hwnd);
    //获取选单句柄
    RECT rc;
    if(statistic==FALSE)
    {
    CheckMenuItem(hmnu,IDM_STATISTIC,MF_CHECKED);
    SetWindowPos(hwnd,0,0,0,600,250,SWP_NOZORDER| SWP_NOMOVE);
    //拉大窗口尺寸
    statistic=TRUE;
    }
    else
    {
    CheckMenuItem(hmnu,IDM_STATISTIC,MF_UNCHECKED);
    SetWindowPos(hwnd,0,0,0,500,250,SWP_NOZORDER | SWP_NOMOVE);
    //收缩窗口尺寸
    statistic=F
  • 作者:SKYHORSEBJ
    email: XUEY@CIDC.COM.CN
    日期:2001-7-4 17:29:48
    当我们使用Visual C++(以下简称VC)开发的应用程序时,若能为应用程序制作一个生动的多媒体封面(如播放一段AVI影视)一定能使应用程序增色不少。有两种方法可以实现这个功能,一种方法是使用底层AVI文件函数,从AVI视频文件中读取视频流,尽管这种方法可以控制整个播放过程,但需要编写大量代码;另一种更简便的实现方法是使用现有的Video for W indows SDK的窗口类MCIWnd(媒体控制界面窗口),这种方法比较易于操作,现介绍如下。

    设计思想及关键技术

    MCIWnd是一个控制多媒体设备(如MIDI、数字视频、VCR以及CD音频设备等)的窗口类,要制作多媒体封面只需创建该类的一个窗口,然后向它发送打开和控制MCI设置的消息。为实现封面效果,我们应当在Initial Instance()函数执行初始化任务之前,对AVI文件进行播放,主要使用Video for Windows SDK的以下几个函数:

    1.MCIWndRegisterClass()注册MCIWnd窗口类。

    2.MCIWndCreate()

    ·函数原型

    HWND MCIWndCreate(HWND hwndParent,HINSTANCE hInstance, DWORD dwStyle,LPSTRs zFile);

    ·实现功能

    该函数创建一个使用MCI设备的窗口,返回MCI设备窗口的句柄。

    ·参数说明

    hwndParent:父窗口句柄,在本应用中父窗口应为NULL;

    hInstance: 当前实例句柄,可以用AfxGet InstanceHandle()函数获得;

    dwStyle: MCIWnd窗口的风格;

    szFile: 打开的MCI设备的名称,在此处设为NULL。

    3.MCIWndOpenO

    ·函数原型

    LONG MCIWndOpen(HWND hwnd,LPVOID szFile,UINT wFlags)

    ·实现功能

    向MCIWnd窗口发送MCIWNDM_OPEN消息,打开某MCI设备,将其关联到
    一个MCIWnd窗口。若调用成功则返回值为0。


    ·参数说明


    hwnd:MCI窗口句柄;


    szFile:MCI文件名;


    wFlags:设备打开方式标识。


    4.MCIWndPlay()


    ·函数原型


    LONG MCIWndPlay(HWND hwnd)


    ·实现功能


    发送MCI_PLAY消息,MCIWnd窗口接收到该消息,播放已经打开的MCI文件。


    ·参数说明


    hwnd:MCI窗口句柄;


    5.MCIWndUseTime()


    该函数将MCI设备的时间格式设置为毫秒;设置其它时间格式可以用函数MCIWndSetTime Format()实现。


    6.MCIWndGetLength()


    该函数向MCIWnd窗口发送MCIWNDM_GETLENGTH消息,根据MCI设备所使用的
    时间格式返回文件的长度。


    7.MCIWndDestroy()


    该函数向窗口发送一个WM_CLOSE消息,窗口接收到该消息之后,关闭所打
    开的MCI文件,并关闭窗口。虽然SDK还提供了一个MCIWndClose函数,但
    该函数只能关闭在MCIWnd窗口中打开的文件,而MCIWnd窗口仍处于开启
    状态,仍可以打开其它MCI文件并进行播放。



    实现步骤






    假设我们已经通过MFC AppWizard(EXE)建立了一个名为Example的工程,则该应用至少包含有CExampleApp、CMainFrame、CExampleView三个类。
    下面我们给这个应用加入多媒体封面,具体实现步骤如下:


    1.编辑Stdafx.h


    在Stdafx.h中放入包含文件可以使用预编译头文件中的所有多媒体信息。由于项目中的每一个文件已经包括了Stdafx.h,所以在其它地方不必包含
    这些多媒体文件。在Stdafx.h中放入包含文件可以使用预编译头文件中的所有多媒体信息。由于项目中的每一个文件已经包括了Stdafx.h,所以在
    其它地方不必包含这些多媒体文件。


    #include<afxwin.h> //MFC core and standard


    components


    #include<afxext.h> //MFC extensions


    #include<vfw.h>


    #pragma comment(lib,"vfw32.lib")


    2.编辑CExampleApp::InitInstance()


    注册MCIWnd窗口类,打开MCIWnd窗口,并播放AVI文件,最后关闭MCIWnd窗口,然后开始应用程序的常规初始化。





    BOOL CExampleApp::InitInstance()



    {



    if(!MCIWndRegisterClass()) //注册MCIWnd窗口类



    return FALSE;



    HWNDm_hAVI //定义一个播放AVI文件的窗口句柄



    m_hAVI=MCIWndCreate(NULL,AfxGetInstanceHandle(),



    MCIWNDF_NOPLAYBAR|



    WS_VISIBLE|



    WS_POPUT,



    NULL); //创建MCIWnd窗口



    if(m_hAVI=NULL)



    return FALSE;



    constCString filename="d://zhp//example.avi" //AVI文件名



    if(filename.GetLength()>0)



    {



    MCIWndOpen(m_hAVI,(LPCSTR)filename,0); //打开设备文件



    if(MCIWndUseTime(m_haAVI)!=0)



    //设置MCI时间格式



    return FALSE;



    long PlayTime=MCIWndGetLength(m_hAVI);



    //获得AVI文件的播放时间长度



    MCIWndPlay(m_hAVI); //播放AVI影视文件



    ::Sleep(PlayTime); //进程休眠



    MCIWndDestroy(m_hAVI); //关闭MCI窗口



    }



    //开始常规初始化



    #ifdef_AFXDLL



    Enable3dControls(); //Call this when using MFC
  • 作者:rick1126
    email: rickzhang@sina.com
    日期:2001-7-12 13:42:06
    << C++ 编程思想 >>

    第01章 对象的演化

    1.0 内容概要

    介绍OOP的基本概念
    讨论OOP的开发方法
    介绍使程序员, 项目和公司使用OOP的策略

    1.1 基本概念

    C++ 包含了比OOP基本概念更多的内容.


    1.1.1 对象: 特性 + 行为

    【类】

    . 描述了一组相同特性(数据元素)和行为(函数)的对象. 就是数据类型.
    . 和基本数据类型相比
    - 类和结构一样是程序员为适应具体问题而自行定义的. 而不是为了描述计算机的基本存储单元
    - 如果说基本数据类型着重描述计算机本身的物理存储的逻辑概念, 类就是程序员为了描述显示世界的具体一类对象的抽象的基本数据类型扩展和应用
    - 这和结构不谋而和或者类本身就是结构的扩展.
    . 所以类就是一种抽象数据类型

    【抽象数据类型】

    . 抽象数据类型是OOP的一个基本概念
    . 程序员使用抽象数据类型可以和使用内部数据类型一样准确工作


    1.1.2 继承: 类型关系

    【类型】

    . 类型说明了一组对象的约束, 这里我理解类型就是术语类(Class), 还说明了与其他类型之间的关系

    【继承】

    . 继承表示了基本类型(基类 Base Class)和派生类型(派生类 Derived Class)之间的相似性
    . 基类拥有派生类的共性特性和行为, 派生类拥有自己的特殊行为和特性

    . 程序员使用基类描述系统中对于一些基本对象的核心认识, 使用派生类描述核心认识的不同途径
    . 程序员可以使用继承建立类的层次结构, 该层次结构使用类术语表达解决问题的方法

    【OOP的特点】

    . 可以分层次的解决问题
    . 使用类可以更接近自然逻辑语言, 而过去使用计算机术语更解决机器本身


    1.1.3 多态性

    【多态性的意义】

    . 程序员使用希望编写不依赖于特殊类型的代码, 是的程序具有扩展性
    . 程序可以根据不同的情况选择不同的方法实现, 以实现"智能化"

    . 编译器不知道连接哪一段代码
    . "早捆绑" -- 前绑定: 使用绝对地址连接函数, 不适用于多态性
    . "晚捆绑" -- 后绑定: 编译器仅仅保证函数调用存在而不确定调用地址

    . 程序员使用 virtual 关键字获得"晚捆绑"特性 -- 虚函数

    1.1.4 操作概念 OOP程序象什么

    【C语言的程序】

    . 过程程序: 数据定义和函数调用
    . 使用中间表示 -- 函数调用和函数实现本身
    . 中间表示容易引起混淆, 因为它们更偏向计算机, 而不是需要解决的问题

    【OOP程序】

    . 只需要掌握一些描述问题空间对象的定义
    . 可读性强, 代码量少


    〖个人理解〗

    其实<< C++编程思想 >>本身就是一本基于C语言角度从计算机特点描述C++语言特点的书籍, 从不同的视角对于C++编程进行理解性的表述, 而不是象某些国内书籍从操作性或者纯OOP角度进行描述, 仅仅将C++作为一种工具, 而忽略了其思维方式和思想内涵. 这大概就是类似国内 "职业" 球员和国外职业球员对于本行的认识问题的区别了.

    下面就是针对OOP的基本特性 -- 封装(抽象), 继承和多态性的描述

    从现实世界中将基本矛盾实体抽象出来进行共性描述, 然后根据特性派生子类, 最终通过多态性赋予对象实体生命力; 我想这大概就是OOP的大致工作方式, 更接近于思维本身, 而更少的考虑机器的特性, 语言本身的特性.

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • C 语 言 图 像 处 理 方 法

      1.图像平移
      图像平移只是改变图像在屏幕上的位置,图像本身并不发生变化。
      假设原图像区域左上角坐标为(x0, y0),右下角坐标为(x1, y1),将图像分别沿x和y轴平移dx和dy,则新图像的左上角坐标为(x0 + dx, y0 + dy),右下角坐标为(x1 + dx, y1 + dy)。坐标平移变换公式为:
      x′ = x + dx
      y′ = y + dy
      在屏幕上实现图像的移动分为四个步骤:
      ⑴ 保存原图像到缓冲区。
      ⑵ 擦除原图像。
      ⑶ 计算平移后的新坐标。
      ⑷ 在新的坐标位置重新显示原图像。
      其中,擦除原图像的方法与图形变换中擦除原图形的方法一致,在实现中仍采用XOR异或方式画图擦除原图像。对于新坐标值的计算还需要考虑边界情况,不要在图像平移后超出允许的屏幕范围。此外,如果采用C函数getimage()和putimage()来保存和恢复图像,则图像的大小不能超过64K。
      2.图像颠倒
      图像颠倒是指把定义好的图像区域上下翻转地显示在屏幕上。分析图像颠倒的过程,可发现每行的图像信息都保持不变,而只是改变了行的顺序,将第一行与最后的第n行相互交换,第二行与第n - 1行交换……,依此类推,从而实现了图像的颠倒。只需采用按行交换的方式,即可方便地修改缓冲区内容,实现图像的颠倒。基本步骤如下:
      (1) 用getimage()保存原图像,并擦除原图像。
      (2) 计算图像的高度,即行数height;计算图像宽度width;计算保存一行图像信息
       height = bottom - top + 1;
      width = right - left + 1;
      linebytes = (width + 7) / 8 * 4;
      (3)利用行交换缓冲区linebuf在图像内存缓冲区中进行信息交换,即把第一行与最末行交换,第2行与第n-1行交换……,依此类推,直至全部交换完毕。
      (4)把交换后的图像缓冲区内容重新显示在屏幕上。
      3.图像镜像变换
      镜像变换是指将指定区域的图像左右翻转地显示在屏幕。分析镜像变换过程可以发现:每行图像信息的处理方式是相同的,而且行顺序不发生变化,只是每一行的像素信息按从左到右的顺序进行了左右颠倒,从而实现了镜像变换。因此,采用按行逐点变换的方式实现图像的镜像。
      首先,对于左上角为(left, top),右下角为(right, bottom)矩形区域图像,给出其中任意点(x0, y0)镜像变换后的新坐标(x′, y′)的坐标变换公式:
      x′ = right - x0 + left
      y′ = y0
      根据以上公式,对各个像素点计算新坐标后,直接把它显示在屏幕的相应位置上。
      如果完全逐点地进行交换,处理一个像素点就要读取一次像素值,从而降低了变换速度。由于像素点是顺序存放在各个bit位上,每读取一个字节就包含了8个像素点的信息,只需设置不同的位屏值bitmask,就可以获得不同像素点的信息。因此采用按行逐字节变换的方式,每读一次就进行8个像素点的变换,以提高变换速度。
      将一矩形区域的图像进行镜像变换的基本步骤如下:
      (1) 用getimage()把图像保存到内存缓冲区,并擦除原图像。
      (2) 计算图像高度,即行数高度height和宽度width;计算保存一行图像信息占用的字节数linebytes。计算公式如下:
      height = bottom - top + 1;
      width = right - left + 1;
      linebyte = (width + 7) / 8 *4;
      (3) 对图像进行镜像。
      (4)释放内存图像缓冲区。
      4.图像旋转
      图像旋转是指把定义的图像绕某一点以逆时针或顺时针方向旋转一定的角度,通常是指绕图像的中心以逆时针方向旋转。
      假设图像的左上角为(left, top),右下角为(right, bottom),则图像上任意点(x0, y0)绕其中心(xcenter, ycenter)逆时针旋转angle角度后,新的坐标位置(x′, y′)的计算公式为:
    xcenter = (right - left + 1) / 2 + left;
    ycenter = (bottom - top + 1) / 2 + top;
    x′ = (x0 - xcenter) cosθ - (y0 - ycenter) sinθ + xcenter;
    y′ = (x0 - xcenter) sinθ + (y0 - ycenter) cosθ + ycenter;
      与图像的镜像变换相类似,也采用按行逐点变换的方式实现图像的旋转,其步骤如下:
      (1) 用getimage()把图像保存到内存缓冲区,并擦除原图像。
      (2) 计算图像高度height,宽度width,及保存一行图像信息占用的字节数linebytes,计算公式与镜像变换的计算公式相同。
      (3) 对图像逐行进行旋转变换。
      (4) 释放内存图像缓冲区。
      值得指出的是,这种处理方法不够。为此可以采用另一种方法:先在图像变换缓冲区中处理完毕后,再将变换后的图像一次显示在屏幕上。这样可以取得较好的显示效果。

    本文出自:《电脑报》1999年03月01日第08期


  • 12.6 应用实例


      通过文件应用实例来加深对文件的认识,使读者在实践中更好地掌握文件的使用。
    例12-14:阅读下列程序,并分析该程序执行结果。
    #include <stdio.h>
    #define LEN 20
    main ( )
    { FILE *fp;
    char s1[LEN], s0[LEN];
    if ( (fp=fopen("try.txt", "w")) == NULL) {
    printf ("Cannot open file./n");
    exit(0);
    }
    printf ("fputs string:");
    gets (s1);
    fputs (s1, fp);
    if ( ferror(fp) )
    printf("/n errors processing file try.txt/n");
    fclose (fp);
    fp = fopen("try.txt", "r");
    fgets (s0, LEN, fp);
    printf ("fgets string:%s/n", s0);
    fclose (fp);
    }
    程序运行时,将从键盘上输入到数组s1中的字符串输出到fp所指定的文件try.txt中,然后再从该文件中读取数据送到数组s0中,最后显示数组s0中的内容。


    例12-15:从键盘输入一行字符串,将其中的小字母全部转换成大写字母,然后输出到一个磁盘文件"test"中保存。
    #include <stdio.h>
    main( )
    { FILE *fp;
    char str[100];
    int i;
    if ((fp=fopen("test", "w")) == NULL) {
    printf ("Cannot open the file./n");
    exit(0);
    }
    printf ("Input a string:");
    gets (str); /* 读入一行字符串 */
    for (i=0; str[i]&&i<100; i++) { /* 处理该行中的每一个字符 */
    if (str[i] >= ’a’ && str[i] <= ’z’) /* 若是小写字母 */
    str[i] -= ’a’ - ’A’; /* 将小写字母转换为大写字母 */
    fputc (str[i], fp); /* 将转换后的字符写入文件 */
    }
    fclose (fp); /* 结束文件输入操作关闭文件 */
    fp = fopen ( "test", "r"); /* 以读方式打开文本文件 */
    fgets ( str, 100, fp); /* 从文件中读入一行字符串 */
    printf("%s/n", str);
    fclose (fp);
    }


    例12-16:编写程序,实现将命令行中指定文本文件的内容追加到另一个文本文件的原内容之后。
    #include <stdio.h>
    main ( argc, argv )
    int argc; char *argv[ ];
    { FILE *fp1, *fp2;
    int c;
    if ( argc != 3) {    /* 若运行程序时指定的参数数量不对 */
       printf ("USAGE:filename1 filename2/n");
      exit(0);
    } /* 以读方式打开第一个文本文件 */
    if ((fp1=fopen(argv[1],"r"))==NULL) /* 位置指针定位于文件开始处 */
    { printf ("Cannot open %s/n", argv[1]);
    exit(1);
    }
    if ((fp2=fopen(argv[2],"a"))==NULL) { /* 以追加方式打开第二个文本文件 */
    printf ("Cannot open %s/n", argv[2]);
    exit(1);
    }
    c = fseek (fp2, 0L, 2); /* 定位第二个文件的文件尾 */
    /* 由于第二个文件是以追加方式打开的,故在输入时系统会自动将新数据加
    在文件的尾部,不论原来文件的位置指针在何处。此语句实际是可以省略的 */
    while ((c=fgetc(fp1)) != EOF) /* 读入第一个文件的数据,直到文件结束 */
    fputc ( c, fp2); /* 将读入的数据写入第二个文件 */
    fclose (fp1); /* 操作结束关闭两个文件 */
    fclose (fp2);
    }
  • 12.3.2 文件的字符串输入/输出函数

      对文件的输入输出,除了前面介绍的以字符为单位进行处理之外,还允许以字符串为单位进行处理,这也被称为"行处理"。
    C语言提供fgets和fputs函数实现文件的按字符串的读写。

    1.字符串输入函数fgets( )

      fgets函数的调用形式:
    fgets (s, n, fp);
    其中:参数s可以是一个字符数组名,或是指向字符串的指针,n为要读取的最多的字符个数;fp是指向该文件的文件型指针。
    fgets函数的功能是:从fp所指向的文件中读取长度不超过n-1个字符的字符串,并将该字符串放到字符数组s中。如果操作正确,函数的返回值为字符数组s的首地址;如果文件结束或出错,则函数的返回值为NULL。
    情况1:从文件中已经读入了n-1个连续的字符,还没有遇到文件结束标志或行结束标志’/n’,
    则:s中存入n-1个字符,串尾以串结束标记’/0’结束。
    情况2:从文件中读入字符遇到了行结束标志’/n’,
    则:s中存入实际读入的字符,串尾为’/n’和’/0’。
    情况3:在读文件的过程中遇到文件尾(文件结束标志EOF),
    则:s中存入实际读入的字符,串尾为’/0’。文件结束标志EOF不会存入数组。
    情况4:当文件已经结束仍然继续读文件,或读取文件内容发生错误,
    则:函数的返回值为NULL,表示文件结束。例如:现有一个有两行字符的ASCII文件,文件打开后,文件的读写位置指针如图12-3所示。


      若有:char s[5];FILE *fp;fp为指向该文件的指针。则多次执行语句"fgets (s, 5, fp);",每次的执行结果如下:
    第1次执行语句fgets (s, 5, fp):
    文件刚打开时,文件的读写位置指针指向了文件的第1个字符,执行语句:fgets (s, 5, fp)之后,从fp指向的文件中读取的字符串是"abcd/n",文件的读写位置指针向前移动了4个字符,字符串在s中的存储形式和文件读写位置指针如图12.4所示。


      第2次执行语句fgets (s, 5, fp):
    从文件的读写位置指针开始,顺序读入字符,遇到’/n’字符后,函数执行完毕。字符串在s中的存储形式和文件读写位置指针如图12.5所示。


      第3次执行语句fgets (s, 5, fp):
    从文件的读写位置指针开始,顺序读入字符,遇到文件结束标记EOF,函数执行完毕,此时,文件的读写位置指针指向了文件最后一个字符的后面。字符串在s中的存储形式和文件读写位置指针如图12-6所示。


      第4次执行语句fgets (s, 5, fp):
    由于文件的读写位置指针已经指向了文件结束标记EOF,所以函数的返回值为NULL,表示文件已经结束,文件的读写位置指针没有变化。文件读写位置指针如图12-7所示。


    例12-5:显示文件内容并加上行号。
    #include <stdio.h>
    main ( )
    { FILE * fp;
    char file[20], str[10];
    int flag=1, i=0; /* flag标志变量,为1:开始新行。i为行号 */
    printf ("Enter filename:");
    scanf("%s",file);
    if ( ( fp = fopen (file, "r")) == NULL ) /* 打开文件 */
    { printf("file open error./n");
    exit (0);
    }
    while ( fgets( str,10,fp )!=NULL ) /* 从文件中读出字符串 */
    { if (flag) printf ("%3d:%s", ++i, str); /* 显示行号 */
    else printf ("%s", str);
    if ( str [strlen(str)-1] == ’/n’ ) flag=1;
    else flag=0;
    }
    fclose (fp);
    }
    本程序的特点是使用一个长度仅为10的小数组来处理文件,程序中充分利用了函数fgets的特点。

    2.字符串输出函数 fputs( )

    fputs函数的调用形式:
    fputs (s, fp);
    其中:s为指向字符串的指针或字符数组名,也可以是字符串常量;fp是指向将要被写入的文件的文件型指针。
    fputs函数的功能是:将s指向的字符串或字符串常量写入fp指向的文件。输出的字符串写入文件时,字符’/0’被自动舍去。函数调用成功,则返回值为0;否则返回EOF。
    例12-6:从键盘输入若干行字符存入磁盘文件file.txt中。
    #include <stdio.h>
    #include <string.h>
    main ( )
    { FILE *fp;
    char str[81];
    if ((fp=fopen("file.txt", "w")) == NULL)
    /* 以写方式打开磁盘文本文件file.txt并判断打开操作正常与否 */
    { printf("Cannot open file./n"); /* 不能正常打开磁盘文件的处理 */
    exit(0);
    }
    while ( strlen(gets(str)) > 0 )
    /* 读入从键盘输入的一行字符,送入str字符数组 */
    { fputs(str, fp); /* 若该字符串非空则送入磁盘文件file.txt中去 */
    fputs("/n", fp);
    }
    fclose (fp); /* 操作结束关闭磁盘文件 */
    }

    例12-7:复制文本文件。
    #include <stdio.h>
    main ( )
    { FILE *fp1, *fp2;
    char file1[20], file2[20], s[10];
    printf ("Enter filename1:");
    scanf("%s",file1);
    printf ("Enter filename2:");
    scanf("%s",file2);
    if ( ( fp1 = fopen (file1, "r")) == NULL ) /* 打开文本文件1 */
    { printf("file1 open error./n");
    exit (0);
    }
    if ( ( fp2 = fopen (file2, "w")) == NULL ) /* 打开文本文件2 */
    { printf("file2 open error./n");
    exit (0);
    }
    while ( fgets( s,10,fp1 ) != NULL ) /* 从文件fp1中读出字符串 */
    fputs ( s, fp2 ); /*
  • 12.4 文件的随机读写操作


      前面介绍的对文件的操作都是顺序读写,即从文件的第一个数据开始,依次进行读写。由指向文件的指针自动移位。但在实际对文件的应用中,还往往需要对文件中某个特定的数据进行处理,这就要求对文件具有随机读写的功能,也就是强制将文件的指针指向用户所希望的指定位置。C语言对文件的定位提供了三个函数。


    12.4.1 改变文件位置指针函数fseek( )
    fseek函数的调用形式:
    fseek(fp,offset,position);
    其中:fp为文件型指针;position为起始点,指出以文件的什么位置为基准进行移动。position的值用整常数表示。ANSI C许它有下列三个值之一:
    0 ── 文件的开头;
    1 ── 文件的当前位置;
    2 ── 文件的末尾。
    offset为位移量,指从起始点position到要确定的新位置的字节数。也就是以起点为基准,向前移动的字节数。ANSI C要求该参数为长整型量。
    fseek函数的功能是:将文件fp的读写位置指针移到离开起始位置(position)的offset字节处的位置;如果函数读写指针移动失败,返回值为-1。
    下面是几个fseek函数调用的实例:
    fseek(fp,50L,0); /* 将位置指针移到文件头起始第50个字节处 */
    fseek(fp,100L,1); /* 将位置指针从当前位置向前(文件尾方向)移动100个字节 */
    fseek(fp,-20L,2); /* 将位置指针从文件末尾向后(文件头方向)移动20个字节 */


    例12-10:实现对一个文本文件内容的反向显示。
    #include <stdio.h>
    main ( )
    { char c;
    FILE *fp;
    if ((fp=fopen("test","r")) == NULL)    /* 以读方式打开文本文件 */
    { printf ("Cannot open file./n");
    exit(1);
    }
    fseek( fp, 0L, 2 ); /* 定位文件尾。注意此时并不是定位到文件的最后一 */
    /* 个字符,而是在定位文件最后一个字符之后的位置 */
    while ((fseek(fp, -1L, 1))!=-1) /* 相对当前位置退后一个字节 */
    { c=fgetc(fp); putchar (c); /* 如果定位成功,读取当前字符并显示 */
    /* 读取字符成功,文件指针会自动移到下一字符位置 */
    if (c==’/n’) /* 若读入是/n字符 */
    fseek(fp, -2L,1); /* 由于DOS在文本文件中要存回车0x0d和换 */
    /* 行0x0a两个字符,故要向前移动两个字节 */
    else fseek (fp, -1L, 1); /* 文件指针向前移动一个字节,使文 */
    } /* 件指针定位在刚刚读出的那个字符 */
    fclose (fp); /* 操作结束关闭文件 */
    }
  • 12.3 文件的顺序读写操作


      文件打开后才可以对文件进行操作。也就是说,文件必须经历打开-操作-关闭的过程。如前所述,C语言对文件的操作都是通过调用标准I/O库函数来实现的。文件操作实际是指对文件的读写。文件的读操作就是从文件中读出数据,即将文件中的数据输入计算机;文件的写操作是向文件中写入数据,即向文件输出数据。实际上对文件的处理过程就是对文件的输入输出过程。在前面已经介绍了C语言对标准设备文件的输入输出函数,本节讨论对磁盘文件的输入输出函数,这类文件及其相应的函数在实际应用和文件处理中占据重要的地位。C语言提供的缓冲式文件处理函数可分为:
    字符输入输出函数 fgetc和fputc
    字符串输入输出函数 fgets和fputs
    格式化输入输出函数 fscanf和fprintf
    数据块输入输出函数 fread和fwrite
    文件定位函数     feek、rewind和ftell
    其它函数       feof、ferror和clearerr。


    12.3.1 文件的字符输入/输出函数
    1.字符输入函数fgetc( )


    fgetc函数的调用形式为:
    ch = fgetc (fp);
    其中fp为文件型指针变量,ch字符变量。
    fgetc函数的功能是:从指定的文件中读取一个字符。即:从fp所指向的文件(该文件必须是以读或读写方式打开的)中读取一个字符返回,读取的字符赋给变量ch。若读取字符时文件已经结束或出错,fputc函数返回文件结束标记EOF,此时EOF的值为-1。
    例如,要从磁盘文件中顺序读入字符并在屏幕上显示,可通过调用fgetc函数实现:
    while ( (c=fgetc(fp)) != EOF )
    putchar(c);
    注意:文件结束标记EOF是不可输出字符,不能在屏幕上显示。因为EOF是在头文件stdio.h中定义的符号常量,其值为-1,而ASCII码中没用-1,可见,用它作文件结束标记是合适的。


    例12-1:在屏幕上显示文本文件的内容。
    #include <stdio.h>
    main ( )
    { FILE *fp;
    char filename[20], ch;
    printf ("Enter filename:");
    scanf("%s",filename); /* 输入文件名 */
    if ( (fp = fopen (filename,"r"))==NULL) /* 打开文件 */
    { printf("file open error./n"); /* 出错处理 */
    exit (0);
    } while ( ( ch = fgetc(fp) )!=EOF) /* 从文件中读字符 */
    putchar(ch); /* 显示从文件读入的字符 */
    fclose (fp); /* 关闭文件 */
    }


    例12-2:使用标准输出文件显示文本文件的内容。
    #include <stdio.h>
    main ( )
    { FILE *fp;
    char filename[20], ch;
    printf ("Enter filename:");
    scanf("%s",filename); /* 输入文件名 */
    if ( (fp = fopen (filename,"r"))==NULL) /* 打开文件 */
    { printf("file open error./n"); /* 出错处理 */
    exit (0);
    } while ( (ch=fgetc(fp) ) != EOF ) /* 从文件中读取字符 */
    fputc(ch,stdout); /* 向标准输出文件中输出(显示) */
    fclose (fp); /* 关闭文件 */
    }


    2.字符输出函数fputc( )


    fputc函数的调用形式为:
    fputc (ch, fp);
    其中:ch是要输出的字符(可为字符常数或字符变量),fp为文件型指针变量。
    fputc函数的功能是:将一个字符输出到指定文件中。即将字符变量ch中的字符输出到fp所指向的文件。若输出操作成功,该函数返回输出的字符;否则,返回EOF。
    例12-3:从键盘输入一字符串,并逐个将字符串的每个字符传送到磁盘文件file中,当输入的字符为"#"时停止输入。
    #include <stdio.h> /* 凡程序中用到标准输入输出函数,必须包含此文件头 */
    main ( )
    { FILE *fp; /* 指向磁盘文件file的指针 */
    char ch; /* 暂存读入字符的字符变量 */
    char filename[15]; /* 存放磁盘文件名的字符数组 */
    scanf("%s", filename); /* 从键盘输入磁盘文件名 */
    if ((fp=fopen(filename,"w"))==NULL)
    /* 以写方式打开文本文件并判定是否能正常打开 */
    { printf("Cannot open file./n"); /* 不能正常打开磁盘文件的处理 */
    exit(0); /* 调用函数exit终止程序运行 */
    }
    while ( (ch=getchar( )) != ’#’ ) /* 判断输入的是否为字符符串结束标志 */
    fputc(ch, fp); /* 读入的字符写入磁盘文件 */
    fclose(fp); /* 操作结束关闭磁盘文件 */
    }


    例12-4:请编程完成文本文件的复制。
    #include <stdio.h>
    main ( )
    { FILE *fp1, *fp2;
    char file1[20], file2[20], ch;
    printf ("Enter filename1:");
    scanf("%s",file1);
    printf ("Enter filename2:");
    scanf("%s",file2);
    if ( (fp1=fopen(file1,"r")) == NULL ) /* 以"只读"方式打开文件1 */
    { printf("file1 open error./n");
    exit (0);
    }
    if ( (fp2=fopen(file2, "w"))== NULL ) /* 以"写"方式打开文件2 */
    { printf("file2 open error./n");
    exit (0);
    }
    while ( ( ch = fgetc(fp1) ) != EOF ) /* 从文件fp1中读字符 */
    fputc (ch, fp2); /* 写入文件fp2中 */
    fclose (fp1); /* 关闭两个文件 */
    fclose (fp2);
    }
  • 12.3.4 文件的数据块输入/输出函数


      这类函数是ANSI C标准对缓冲文件系统所做的扩充,以方便文件操作实现一次读写一组数据的功能。例如采用这种方式对数组和结构进行整体的输入输出是比较方便的。


    1.文件数据块读函数fread( )
    fread函数的调用形式:
    fread ( buffer, size, count, fp);
    其中:buffer是一个指针,是指向输入数据存放在内存区的起始地址;size是要输入的字节数;count是要输入大小为size个字节的数据块的个数;fp是文件指针。
    fread函数的功能是:对fp所指向的文件读取count次,每次读取一个大小为size的数据块,将读取的各数据块存到buffer指向的内存区。该函数的返回值是实际读取的count的值。


    2.文件数据块写函数fwrite( )
    fwrite函数的调用形式:
    fwrite ( buffer, size, count, fp);
    fwrite函数的参数及其功能与fread函数类似,只是对文件的操作而言是互逆的,一个是读取,一个是写入。


    例12-9:从键盘输入3个学生的数据,将它们存入文件student;然后再从文件中读出数据,显示在屏幕上。
    #include <stdio.h>
    #define SIZE 3
    struct student /* 定义结构 */
    { long num;
    char name[10];
    int age;
    char address[10];
    } stu[SIZE], out;
    void fsave ( )
    { FILE *fp;
    int i;
    if (( fp=fopen("student","wb")) == NULL ){ /* 以二进制写方式打开文件 */
    printf ("Cannot open file./n"); /* 打开文件的出错处理 */
    exit(1); /* 出错后返回,停止运行 */
    }
    for (i=0; i<SIZE; i++) /* 将学生的信息(结构)以数据块形式写入文件 */
    if (fwrite(&stu[i], sizeof(struct student), 1, fp) != 1)
    printf("File write error./n"); /* 写过程中的出错处理 */
    fclose (fp); /* 关闭文件 */
    }
    main ( )
    { FILE *fp;
    int i;
    for (i=0; i<SIZE; i++) { /* 从键盘读入学生的信息(结构) */
    printf("Input student %d:", i+1);
    scanf ("%ld%s%d%s",
    &stu[i].num, stu[i].name, &stu[i].age, stu[i].address );
    }
    fsave( ); /* 调用函数保存学生信息 */
    fp = fopen ("student", "rb"); /* 以二进制读方式打开数据文件 */
    printf (" No. Name Age Address/n");
    while ( fread(&out, sizeof(out), 1, fp) ) /* 以读数据块方式读入信息 */
    printf ("%8ld %-10s %4d %-10s/n",
    out.num, out.name, out.age, out.address );
    fclose(fp); /* 关闭文件 */
    }
  • 12.3.3 文件的格式化输入输出函数


      前面的章节中介绍了scanf和printf两个格式化输入输出函数,它们适用于标准设备文件。C标准函数库还提供了fscanf和fprintf两个格式化输入输出函数,以满足磁盘文件格式化输入输出的需要。


    1.格式化输入函数 fscanf( )
    fscanf函数的调用形式:
    fscanf (fp, 格式控制串, 输入列表);
    其中:fp指向将要读取文件的文件型指针,格式控制串和输入列表的内容、含义及对应关系与第二章中介绍的scanf函数相同。
    fscanf函数的功能是:从fp指向的文件中,按格式控制符读取相应数据赋给输入列表中的对应变量地址中。
    例如,
    fscanf (fp, "%d,%f", &i, &t);
    完成从指定的磁盘文件上读取ASCII字符,并按"%d"和"%f"型格式转换成二进制形式的数据送给变量i和t。


    2.格式化输出函数 fprintf( )
    fprintf函数的调用形式:
    fprintf (fp, 格式控制串, 输出列表);
    其中:fp指向将要写入文件的文件指针,格式控制串和输出列表的内容及对应关系与前面章节中介绍的printf函数相同。
    fprintf函数的功能是:将输出列表中的各个变量或常量,依次按格式控制符说明的格式写入fp指向的文件。该函数调用的返回值是实际输出的字符数。


    例12-8.C:从键盘输入一个字符串和一个十进制整数,将它们写入test文件中,然后再从test文件中读出并显示在屏幕上。
    #include <stdio.h>
    main( )
    { char s[80];
    int a;
    FILE *fp;
    if ((fp=fopen("test", "w")) == NULL) { /* 以写方式打开文本文件 */
    printf ("Cannot open file./n");
    exit(1);
    }
    fscanf (stdin, "%s%d", s, &a); /* 从标准输入设备(键盘)上读取数据 */
    fprintf(fp, "%s %d", s, a); /* 以格式输出方式写入文件 */
    fclose (fp); /* 写文件结束关闭文件 */
    if ((fp=fopen("test", "r")) == NULL) { /* 以读方式打开文本文件 */
    printf ("Cannot open file./n");
    exit(1);
    }
    fscanf (fp, "%s%d", s, &a); /* 以格式输入方式从文件读取数据 */
    fprintf(stdout, "%s %d/n", s, a); /* 将数据显示到标准输出设备(屏幕)上 */
    fclose(fp); /* 读文件结束关闭文件 */
    }

  • 12.5 文件操作的状态和出错检测


      由于C语言中对文件的操作都是通过调用有关的函数来实现,所以用户必须直接掌握函数调用的情况,特别是掌握函数调用是否成功。为此,C语言提供了两种手段来反映函数调用的情况和文件的状态。其一,由函数的返回值可以知道文件调用是否成功。例如:调用fgets、fputs、fgetc、fputc等函数时,若文件结束或出错,将返回EOF;在调用fread、fopen、fclose等函数时,若出错,则返回NULL。其二,由C函数库提供对文件操作状态和操作出错的检测函数。这些检测函数包括:feof、ferror和clearerr函数。


    12.5.1 文件状态检测函数feof( )
    feof函数的一般调用形式为:
    feof (fp);
    其中:fp为文件指针。
    函数feof的功能是:测试fp所指的文件的位置指针是否已到达文件尾(文件是否结束)。如果已到达文件尾,则函数返回非0值;否则返回0,表示文件尚未结束。
  • 12.4.2 位置指针重返文件头函数rewind( )


    rewind函数的调用形式:
    rewind(fp);
    其中:fp为文件型指针。
    rewind函数的功能是:使fp指定的文件的位置指针重新定位到文件的开始位置。


    例12-11:在屏幕上显示文件file1.c的内容,并将文件file1.c复制到文件file2.c。
    #include <stdio.h>
    main( )
    { FILE *fp1,*fp2;
    fp1=fopen("file1.c","r");
    fp2=fopen("file2.c","w");
    while ( !feof(fp1) ) /* 完成在屏幕上显示文件file1.c的内容 */
    putchar ( fgetc(fp1) ); /* 函数feof判断文件是否结束 */
    rewind(fp1); /* 完成操作1)后,文件file1.c的指针已指到文件的末尾,
    为了完成操作2),使file1.c的位置指针重返回文件头 */
    while(!feof(fp1))
    fputc ( fgetc(fp1),fp2 ); /* 把文件file1.c的内容复制到file2.c中 */
    fclose(fp1); /* 操作结束分别关闭两个文件 */
    fclose(fp2);
    }

            

  • 12.4.3 位置指针当前值函数 ftell( )


      ftell函数的调用形式:
    ftell(fp);
    其中:fp为文件指针。
    ftell函数的功能是:得到fp所指向文件的当前读写位置,即位置指针的当前值。该值是一个长整型数,是位置指针从文件开始处到当前位置的位移量的字节数。如果函数的返回值为-1L,表示出错。


    例12-12:首先建立文件data.txt,检查文件指针位置;将字符串"Sample data"存入文件中,再检查文件指针的位置。
    #include <stdio.h>
    main( )
    { FILE *fp; long position;
    fp=fopen("data.txt", "w"); /* 打开文件 */
    position=ftell(fp); /* 取文件位置指针 */
    printf ("position=%ld/n", position);
    fprintf(fp, "Sample data/n"); /* 向文件中写入长度为12的字符串 */
    position=ftell(fp); /* 取文件位置指针 */
    printf ("position=%ld/n", position);
    fclose(fp);
    }
    运行程序,结果如下:
    position=0 /* 打开文件时位置指针在文件第一个字符之前 */
    position=13 /* 写入字符串后位置指针在文件最后一个字符之后 */
  • 12.5.3 清除错误标志函数 clearer( )


    clearerr函数的一般调用形式为:
    clearerr (fp);
    其中:fp为文件指针。
    函数clearerr的功能是:清除fp所指的文件的错误标志。即将文件错误标志和文件结束标记置为0。
    在用feof和ferror函数检测文件结束和出错情况时,遇到文件结束或出错,两个函数的返回值均为非0。对于出错或已结束的文件,在程序中可以有二种方法清除出错标记:调用clearerr函数清除出错标记,或者对出错文件调用一个正确的文件I/O操作函数。


    例12-13:从键盘上输入一个长度小于20的字符串,将该字符串写入文件"file.dat"中,并测试是否有错。若有错,则输出错误信息,然后清除文件出错标记,关闭文件;否则,输出输入的字符串。
    #include "stdio.h"
    #include "string.h"
    #define LEN 20
    main ( )
    { int err;
    FILE *fp;
    char s1[LEN];
    if ( (fp = fopen("file.dat", "w") ) == NULL ) { /* 以写方式打开文件 */
    printf ("Can’t open file1.dat/n");
    exit(0);
    }
    printf ("Enter a string:");
    gets (s1); /* 接收从键盘输入的字符串 */
    fputs (s1, fp) ; /* 将输入的字符串写入文件 */
    err = ferror(fp); /* 调用函数ferror */
    if ( err ) { /* 若出错则进行出错处理 */
    printf("file.dat error:%d/n", err);
    clearerr(fp); /* 清除出错标记 */
    fclose (fp);
    }
    fclose (fp);
    fp = fopen("file.dat", "r"); /* 以读方式打开文件 */
    if ( err = ferror(fp) )
    { printf("Open file.dat error %d/n", err);
    fclose (fp);
    }
    else
    { fgets (s1, LEN, fp); /* 读入字符串 */
    if ( feof(fp) && strlen(s1)==0 ) /* 若文件结束且读入的串长为0 */
    printf("file.dat is NULL./n"); /* 则文件为空,输出提示 */
    else
    printf("Output:%s/n", s1); /* 输出读入的字符串 */
    fclose(fp);
    }
    }

         
  • 2005-09-20

    打开文件函数 - [C语言]

    12.2.3 打开文件函数fopen( )


      C语言用函数fopen( )实现打开文件操作。fopen函数的调用形式是:
    FILE * fp;
    fp = fopen(文件名, 文件使用方式);
    其中,FILE是前面介绍的文件类型;fp是一个指向FILE类型的指针变量,即指向被打开的文件。文件名即为所要打开的文件名称。文件使用方式用具有特定含义的符号表示,见表12-1所示。




      函数的功能是:以指定的方式打开指定的文件。
    (1)用"r"方式打开文件时,只能从文件向内存输入(读入)数据,而不能从内存向该文件输出(写)数据。以"r"方式打开的文件应该已经存在,不能用"r"方式打开一个并不存在的文件(即输入文件),否则出错。


    (2)用"w"方式打开文件时,只能从内存向该文件输出(写)数据,而不能从文件向内存输入数据。如果该文件原来不存在,则打开时建立一个以指定文件名命名的文件。如果原来的文件已经存在,则打开时将文件删空,然后重新建立一个新文件。


    (3)如果希望向一个已经存在的文件的尾部添加新数据(保留原文件中已有的数据),则应用"a"方式打开。但此时该文件必须已经存在,否则会返回出错信息。打开文件时,文件的位置指针在文件末尾。


    (4)用"r+"、"w+"、"a+"方式打开的文件可以输入输出数据。用"r+"方式打开文件时,该文件应该已经存在,这样才能对文件进行读/写操作。用"w+"方式则建立一个新文件,先向此文件中写数据,然后可以读取该文件中的数据。用"a+"方式打开的文件,则保留文件中原有的数据,文件的位置指针在文件末尾,此时,可以进行追加或读操作。


    (5)如果不能完成文件打开操作,函数fopen将返回错误信息。出错的原因可能是:用"r"方式打开一个并不存在的文件;磁盘故障;磁盘已满无法建立新文件等。此时fopen函数返回空指针值NULL(NULL在stdio.h文件中已被定义为0)。


    (6)用以上方式可以打开文本文件或二进制文件。ANSI C规定可用同一种缓冲文件系统来处理文本文件和二进制文件。


    (7)在用文本文件向内存输入时,将回车符和换行符转换为一个换行符,在输出时将换行符换成回车和换行两个字符。在用二进制文件时,不进行这种转换,在内存中的数据形式与输出到外部文件的数据形式完全一致,一一对应。
    在常见的文件打开操作语句中,通常需要同时判断打开过程是否出错。例如,以只读方式打开文件名为filename的文件,则语句如下:
    if ( ( fp = fopen ("filename", "r") ) == NULL )
    { printf ("Cannot open file./n"); /* 如果文件出错显示提示信息 */
    exit (0); /* 调用exit函数终止程序运行 */
    }


    由系统打开的三个标准文件stdin、stdout和stderr,在使用的时候不需要调用fopen函数打开,可以直接使用它们的文件指针进行操作。
    初学者应当注意的问题是,打开文件时设定的文件使用方式与后面对该文件的实际使用情况不一致,会使系统产生错误。例如:以"r"方式打开已存在的文件,要进行写操作是不行的,而应当将"r"改为"r+"或"a+"。
  • 12.5.2 报告文件操作错误状态函数 ferror( )


    ferror函数的一般调用形式为:
    ferror (fp);
    其中:fp为文件指针。
    函数ferror的功能是:测试fp所指的文件是否有错误。如果没有错误,返回值为0;否则,返回一个非0值,表示出错。
  • C语言中要实现对文件的随机存取,需要用到两个函数fseek()、ftell()。下面通过一个反转显示指定文件的程序来介绍这两个函数的用法。

    reserve.c:



    #include <stdio.h>

    #include <stdlib.h>



    #define CNTL_Z ’/032’ //DOS 文本中的文件结尾标记

    #define SLEN 50



    int main(int argc, char *argv[])

    {

    char file[SLEN];

    char ch;

    FILE *fp;

    long count, last;



    puts("Enter the name of the file to be processed: ");

    gets(file);

    if( (fp = fopen(file, "rb")) == NULL ) //只读和二进制模式

    {

    printf("reverse can’t open %s/n", file);

    exit(1);

    }



    fseek(fp, 0L, SEEK_SET); //定位在文件开头处

    last = ftell(fp);

    printf("fseek(fp, 0L, SEEK_SET) , fteel(p): %d/n", last);



    fseek(fp, 0L, SEEK_END); //定位在文件结尾处

    last = ftell(fp);

    printf("fseek(fp, 0L, SEEK_END) , fteel(p): %d/n", last);



    for(count = 1L; count <= last; count++)

    {

    fseek(fp, -count, SEEK_END);

    ch = getc(fp);



    if(ch != CNTL_Z && ch != ’/r’)

    {

    putchar(ch);

    }

    }

    putchar(’/n’);

    fclose(fp);



    system("PAUSE");

    return 0;

    }




    假定一个文件test.txt内容为:



    1234567890

    1234567890

    1234567890

    1111111112

    2222222223

    3333333334




    执行reserve来进行反转显示:



    Enter the name of the file to be processed:

    test.txt

    fseek(fp, 0L, SEEK_SET) , fteel(p): 0

    fseek(fp, 0L, SEEK_END) , fteel(p): 72



    4333333333

    3222222222

    2111111111

    0987654321

    0987654321

    0987654321




    下面,我们来解释一下fseek()和ftell()是如何工作的。

    l fseek()函数

    fseek(移动文件流的读写位置)

    相关函数
    rewind,ftell,fgetpos,fsetpos,lseek

    表头文件
    #include<stdio.h>

    定义函数
    int fseek(FILE * stream,long offset,int whence);

    函数说明
    fseek()用来移动文件流的读写位置。参数stream为已打开的文件指针,参数offset为根据参数whence来移动读写位置的位移数。

    参数
    whence为下列其中一种:
    SEEK_SET从距文件开头offset位移量为新的读写位置。SEEK_CUR 以目前的读写位置往后增加offset个位移量。
    SEEK_END将读写位置指向文件尾后再增加offset个位移量。
    当whence值为SEEK_CUR 或SEEK_END时,参数offset允许负值的出现。
    下列是较特别的使用方式:
    1) 欲将读写位置移动到文件开头时:fseek(FILE *stream,0,SEEK_SET);
    2) 欲将读写位置移动到文件尾时:fseek(FILE *stream,0,0SEEK_END);

    返回值
    当调用成功时则返回0,若有错误则返回-1,errno会存放错误代码。

    附加说明
    fseek()不像lseek()会返回读写位置,因此必须使用ftell()来取得目前读写的位置。




    l ftell()函数

    ftell(取得文件流的读取位置)

    相关函数
    fseek,rewind,fgetpos,fsetpos

    表头文件
    #include<stdio.h>

    定义函数
    long ftell(FILE * stream);

    函数说明
    ftell()用来取得文件流目前的读写位置。参数stream为已打开的文件指针。

    返回值
    当调用成功时则返回目前的读写位置,若有错误则返回-1,errno会存放错误代码。

    错误代码
    EBADF 参数stream无效或可移动读写位置的文件流。

    范例
    参考fseek()。




    通过fseek()、ftell()两个函数,我们就可以随意访问文件的任何位置了,想了想好像操作文件就这么easy,实在也没有更多可说的了。对了,fseek()和ftell()存在一个潜在的问题就是他们限制文件的大小只能在long类型的表示范围以内,也就是说通过这种方式,只能打开2,000,000,000字节的文件,不过在绝大多数情况下似乎也已经够用了。如果需要打开更大的文件,你需要用到fgetpos()、fsetpos()函数了,那是另一个命题了。
  • 2005-09-20

    输入设备 - [DOS]

    下载地址http://upload.yourblog.org/20059/kimnan.20050920224807.rar
  • 2005-09-20

    输入输出设备 - [DOS]

    下载地址http://upload.yourblog.org/20059/kimnan.20050920224720.rar
  • 2005-09-20

    DOS和BIOS接口 - [DOS]

    下载地址http://upload.yourblog.org/20059/kimnan.20050920224354.rar
  • 2005-09-20

    动态的DOS - [DOS]

    下载地址http://upload.yourblog.org/20059/kimnan.20050920224147.rar
  • 2005-09-20

    DOS系统结构 - [DOS]

    下载地址http://upload.yourblog.org/20059/kimnan.20050920223920.rar
  • 2005-09-20

    DOS概述 - [DOS]

    3页
    第一部分DOS概述
    第1章DOS简介
    在进一步阅读本书之前,首先要清楚什么是DOS。本章首先扼要地介绍DOS操作系
    统,再简短地介绍操作系统的历史,以说明DOS产生的历史渊源。本章的内容还将涉及
    DOS结构及接口。因为这是一个非常粗略的介绍,所以不要关注一些尚不清楚的术语,因
    为在本章之后的其它各章还会对其进行更详细的解释。
    1.1 什么是DOS
    DOS由四个基本模块组成:
    ·引导记录(The boot record)此记录起始于每个磁盘的0道、0扇区、第1面上,
    是由DOS FORMAT命令格式化磁盘时放入的。对于硬盘,引导记录位于DOS分
    区的第一个扇区内。这个需要一个扇区空间的记录,标识了该磁盘,并含有用于引
    导该磁盘的初始化程序。
    · BIOS 基本输入/输出系统(BIOS)是存放在ROM中的。这个面向物理设备的
    低层接口,使得各种软件可以透明地使用各种变幻莫测的硬件设备。对于DOS它
    也通过从磁盘中调入I/O来扩展其功能。
    · DOS程序 DOS是通过两个程序来实现的。一个是O/I系统,它是从磁盘中调
    入的接口模块,用来扩展ROM BIOS的功能,并包含有标准的设备驱动程序集。
    另一个文件是磁盘操作系统(DOS),它是一个所有运行在计算机上的程序的高层
    接口,而不管该程序是否使用磁盘。
    ·命令处理程序(The command processor)大多数人认为该模块即是DOS。命令
    处理程序是人们工作在该系统下运行DOS服务的标准界面,它产生命令提示符
    (C>),接收命令,并执行用户向系统请求执行的各项任务。
    各个模块将在第2章“DOS系统结构”和第3章“动态的DOS” 中详细介绍。然而我们
    在这里还是作些基本的介绍。
    BIOS提供了一系列功能,程序员可以利用这些功能来完成各种操作而不必关心底层
    硬件的细节。在本书中,我们将利用BIOS来执行各种实例程序里的必需操作。在本书第
    五部分“参考手册”中,按顺序详细介绍了BIOS中每个功能的作用。
    虽然BIOS功能非常强,但它离完整性还差得很远。建立在BIOS基础上的DOS平
    台,为编程者提供了很多必要的服务。在通用的操作系统(如DOS)成为现实之前的早期

    4页
    个人计算机上,程序员所写的程序中,常常包含有与DOS功能等同的内容。因此,跟踪应
    用程序的过程,也是非常复杂和可怕的。
    由于DOS功能的编程已由Microsoft(和其它厂商)完成,DOS已成为读者在程序开
    发中的伙伴。虽然不能认为DOS中的一切都工作得非常正确而不会出错,但可以假定
    DOS是坚实的(除非有其它已证实的理由)。第五部分则包含了各种DOS功能的一项一
    项详细介绍。
    1.2 DOS的历史
    多少年以来,DOS一直被当作微型计算机的最原始的操作系统。今天的DOS,所拥有
    的用户数比任何其它操作系统要多得多。它已变得非常复杂,是一个带有各种工具和应用
    程序来满足各种需要的操作环境。
    DOS富有远见卓识之处就在于,它能处理各种复杂的微处理器的复杂特性,如80386
    和80486等。将来的DOS版本,甚至可以处理多任务,支持多用户,尽管Microsoft公司还
    没有明确地提供这方面的信息。一些人可能会对此心存疑问,特别是出现了新一代操作系
    统—Windows和Windows NT之后。
    DOS首先是由Seattle Computer Products公司为其计算机将86—DOS第一次注册的。
    DOS的最原始程序是由Tim Paterson编写的。它开始于1980年8月,第一个产品诞生于
    该年的8月。那时,Digited Research的CP/M操作系统在微机操作系统领域有着广泛的
    应用。86-DOS的一个特殊设计是CP/M的应用程序很容易转换过来,它保留了同样的文
    件控制块结构和功能,因此,可以自动地将CP/M的程序转换到86—DOS上。
    因为86-DOS只能工作在1980年刚上市的8086/8088 CPU芯片上,所以很少有人知
    道它的存在。但那些已经从8位的8088/Z80标准和CP/M系统升级到s-100系统的人
    们,已经出现了86-DOS的用途,同时,Seattle Computer Products公司也已建立了拥有几
    个多客户的市场基础,其中至少还包括一两个硬件制造广商,与此同时,Microsoft公司也
    在与SCP公司接触,要他们为一个未知的公司研制一个新的版本。而在当时,没有人(除
    Microsoft公司外)知道,IBM也在寻找一种操作系统。在1981年1月,Paterson知道了那
    个客户的名字,并且Microsoft公司已经将从86-DOS分离出的版本注册在他们自己的名
    下,那年4月,Paterson离开了Seattle Computer Products公司,加盟到了Microsoft,以后
    几个月,他按照IBM的需要,进一步裁剪了该系统。
    在1981年7月,Microsoft从Seattle Computer Products公司那里,以相当低的价钱
    (少于10万美元)购买了86-DOS的全部版权。以后,SCP公司对此交易提出过起诉,最后
    法院判决Microsoft支付了数百万美元的补偿。其结果是Microsoft公司毫无异议地永远
    拥有了最流行的操作系统的版权。
    当1981年8月10日IBM发布PC时,Microso
  • 2005-09-20

    字符屏幕函数 - [C语言]

    字符屏幕函数

    Turbo C2.0的字符屏幕函数主要包括文本窗口大小的设定、窗口颜色的设置、
    窗口文本的清除和输入输出等函数。

    1.文本窗口的定义
    Turbo C2.0默认定义的文本窗口为整个屏幕, 共有80列(或40列)25行的文本
    单元, 每个单元包括一个字符和一个属性, 字符即ASCII 码字符, 属性规定该字
    符的颜色和强度。
    Turbo C2.0可以定义屏幕上的一个矩形域作为窗口, 使用window()函数定义。
    窗口定义之后, 用有关窗口的输入输出函数就可以只在此窗口内进行操作而不超
    出窗口的边界。
    window()函数的调用格式为:
    void window(int left, int top, int right, int bottom);
    该函数的原型在conio.h 中 (关于文本窗口的所有函数其头文件均为conio.h,
    后面不再说明)。 函数中形式参数(int left, int top)是窗口左上角的坐标,
    (int right, int bottom)是窗口的右下角坐标, 其中(left, top)和(right,
    bottom) 是相对于整个屏幕而言的。 Turbo C 2.0规定整个屏幕的左上角坐标为
    (1, 1), 右下角坐标为(80, 25)。并规定沿水平方向为 X轴, 方向朝右; 沿垂直
    方向为 Y轴, 方向朝下。若window()函数中的坐标超过了屏幕坐标的界限, 则窗
    口的定义就失去了意义, 也就是说定义将不起作用, 但程序编译链接时并不出错。
    另外, 一个屏幕可以定义多个窗口, 但现行窗口只能有一个(因为DOS为单任
    务操作系统), 当需要用另一窗口时, 可将定义该窗口的window() 函数再调用一
    次, 此时该窗口便成为现行窗口了。
    如要定义一个窗口左上角在屏幕(20, 5)处, 大小为30列15行的窗口可写成:
    window(20, 5, 50, 25);

    2. 文本窗口颜色的设置
    文本窗口颜色的设置包括背景颜色的设置和字符颜色的设置, 使用的函数及
    其调用格式为:
    设置背景颜色: void textbackground(int color);
    设置字符颜色: void textcolor(int color);
    有关颜色的定义见表1。
    表1. 有关颜色的定义
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━
    符号常数 数值 含义 字符或背景
    ───────────────────────────
    BLACK 0 黑 两者均可
    BLUE 1 兰 两者均可
    GREEN 2 绿 两者均可
    CYAN 3 青 两者均可
    RED 4 红 两者均可
    MAGENTA 5 洋红 两者均可
    BROWN 6 棕 两者均可
    LIGHTGRAY 7 淡灰 两者均可
    DARKGRAY 8 深灰 只用于字符
    LIGHTBLUE 9 淡兰 只用于字符
    LIGHTGREEN 10 淡绿 只用于字符
    LIGHTCYAN 11 淡青 只用于字符
    LIGHTRED 12 淡红 只用于字符
    LIGHTMAGENTA 13 淡洋红 只用于字符
    YELLOW 14 黄 只用于字符
    WHITE 15 白 只用于字符
    BLINK 128 闪烁 只用于字符
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━
    上表中的符号常数与相应的数值等价, 二者可以互换。例如设定兰色背景可
    以使用textbackground(1), 也可以使用textbackground(BLUE), 两者没有任何
    区别, 只不过后者比较容易记忆, 一看就知道是兰色。
    Turbo C另外还提供了一个函数, 可以同时设置文本的字符和背景颜色, 这
    个函数的调用格式为:
    void textattr(int attr);
    其中: attr的值表示颜色形式编码的信息, 每一位代表的含义如下:
    位 7 6 5 4 3 2 1 0
    B b b b c c c c
    ↓ ┕━━━┙ ┖─────┘
    闪烁 背景颜色 字符颜色
    字节低四位cccc设置字符颜色(0到15), 4~6三位bbb设置背景颜色(0到7),
    第7位B设置字符是否闪烁。假如要设置一个兰底黄字, 定义方法如下:
    textattr(YELLOW+(BLUE<<4));
    若再要求字符闪烁, 则定义变为:
    textattr(128+YELLOW+(BLUE<<4);
    注意:
    (1) 对于背景只有0 到7 共八种颜色, 若取大于7 小于15的数, 则代表的
    颜色与减 7后的值对应的颜色相同。
    (2) 用textbackground()和textcolor() 函数设置了窗口的背景与字符颜
    色后, 在没有用clrscr()函数清除窗口之前, 颜色不会改变, 直到使用了函数
    clrscr(), 整个窗口和随后输出到窗口中的文本字符才会变成新颜色。
    (3) 用textattr()函数时背景颜色应左移4位, 才能使3位背景颜色移到正
    确位置。
    下面这个程序使用了关于窗口大
  • Turbo C 程序设计初步

    本节主要介绍Turbo C程序设计的基本步骤及如何编译、调试和运行源程序。
    并给出Turbo C的常用编辑命令。最后介绍Turbo C编译、连接和运行时的常见错
    误。

    一、Turbo C程序设计基本步骤

    程序设计方法包括三个基本步骤:
    第一步: 分析问题。
    第二步: 画出程序的基本轮廓。
    第三步: 实现该程序。
    3a. 编写程序
    3b. 测试和调试程序
    3c. 提供数据打印结果
    下面, 我们来说明每一步的具体细节。

    第一步: 分析问题
    在这一步, 你必须:
    a. 作为解决问题的一种方法, 确定要产生的数据(输出)。作为这一子步的
    一部分, 你应定义表示输出的变量。
    b. 确定需产生输出的数据(称为输入), 作为这一子步的一部分, 你应定义
    表示输入的变量。
    c. 研制一种算法, 从有限步的输入中获取输出。 这种算法定义为结构化的
    顺序操作, 以便在有限步内解决问题。就数字问题而言, 这种算法包括获取输出
    的计算, 但对非数字问题来说, 这种算法包括许多文本和图象处理操作。

    第二步: 画出程序的基本轮廓
    在这一步, 你要用一些句子(伪代码)来画出程序的基本轮廓。每个句子对应
    一个简单的程序操作。对一个简单的程序来说, 通过列出程序顺序执行的动作,
    便可直接产生伪代码。然而, 对复杂一些的程序来说, 则需要将大致过程有条理
    地进行组织。对此, 应使用自上而下的设计方法。
    当使用自上而下的设计方法时, 你要把程序分割成几段来完成。列出每段要
    实现的任务, 程序的轮廓也就有了, 这称之为主模块。当一项任务列在主模块时,
    仅用其名加以标识, 并未指出该任务将如何完成。这方面的内容留给程序设计的
    下一阶段来讨论。将程序分为几项任务只是对程序的初步设计。整个程序设计归
    结为下图所示的流程图1.。
    ┏━━━━━━━━━━━━━━━┓
    ┃ 主模块 ┃
    ┏━━━━━━━┓ ┃ 输入数据 ┃
    ┃ 主模块 ┃ ┃ 计算购房所需的金额 ┃
    ┃ ┃ ┃ 计算装修所需的金额 ┃
    ┃ 任务1 ┃ ┃ 计算总金额 ┃
    ┃ 任务2 ┃ ┃ 输出计算结果 ┃
    ┃ 任务3 ┃ ┃ ┃
    ┃ 任务4 ┃ ┗━━━━━━━┳━━━━━━━┛
    ┃ ┃ ┏━━━━━┳━━━━━╋━━━━┳━━━━━┓
    ┃ ┃ ┏━━┻━┓┏━━┻━┓┏━━┻━┓┏━┻━┓┏━━┻━┓
    ┗━━━━━━━┛ ┃输入数据┃┃购房额..┃┃装修额..┃┃总额..┃┃输出结果┃
    ┗━━━━┛┗━━━━┛┗━━━━┛┗━━━┛┗━━━━┛
    图1. 程序初步设计 图2. 第二级程序设计

    如果把主模块的每项任务扩展成一个模块, 并根据子任务进行定义的话, 那
    么, 程序设计就更为详细了(见图2.)。这些模块称为主模块的子模块。程序中许
    多子模块之间的关系可象图2.中那样归结为一张图。这种图称为结构图。
    要画出模块的轮廓, 你可不考虑细节。如果这样的话, 你必须使用子模块,
    将各个模块求精, 达到第三级设计。继续这一过程, 直至说明程序的全部细节。
    这一级一级的设计过程称为逐步求精法。在编写程序之前, 对你的程序进行逐步
    求精, 对你来说, 是很好的程序设计实践, 会使你养成良好的设计习惯。
    我们则才描述了程序设计中自上而下的设计方法。实际上就是说, 我们设计
    程序是从程序的"顶部"开始一直考虑到程序的"底部"。

    第三步: 实现该程序
    程序设计的最后一步是编写源码程序。 在这一步, 把模块的伪代码翻译成
    Turbo C语句。
    对于源程序, 你应包含注释方式的文件编制, 以描述程序各个部分做何种工
    作。此外, 源程序还应包含调试程序段, 以测试程序的运行情况, 并允许查找编
    程错误。一旦程序运行情况良好, 可去掉调试程序段, 然而, 文件编制应做为源
    程序的固定部分保留下来, 便于你或其他人维护和修改。
    二、源程序的输入、编译和运行

    C语言是一种中级语言, 用户用C语言编写的程序称为源程序, 存放用C 语言
    所写源程序文件名字最后的两个字符一般必须为".c"。计算机硬件不能直接执行
    源程序, 必须将源程序翻译成二进制目标程序。翻译工作是由一个程序完成的,
    这个程序称为编译程序, 翻译的过程称为编译, 编译的结果称为目标程序, 存放
    目标程序文件名字紧后的字符一般为".OBJ"或".O"。程序翻译成目标程序后, 便
    可进行连接。"连接"的目的是使程序变成在计算机上可以执行的最终形式。在这
    一阶段, 从系统程序库来的程  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值