我的一个关于文件的程序 - [C语言]

时间:2023-02-21 10:21:27

 

  • 2005-09-05

    我的一个关于文件的程序 - [C语言]

    #include<stdio.h>
    void main()
    {
    char ch;
    FILE* fp;
    if((fp=fopen("test.txt","r"))==NULL)
    {
    printf("error");
    exit(1);
    }
    fseek(fp,0L,2);
    while((fseek(fp,-1L,1))!=-1)
    {
    ch=fgetc(fp);
    putchar(ch);
    if(ch==’/n’)
    fseek(fp,-2L,1);else fseek(fp,-1L,1);
    }
    fclose(fp);
    }


  • 2005-09-01

    应用ATL的一个例子 - [VC专栏]




    ---- 上 面 介 绍 了 使 用ATL 创 建 一 个COM 服 务 程 序 的 基 本 过 程。 在 介 绍 过 程 中, 我 们 实 际 上 已 经 生 成 了 一 个COM 服 务 程 序 的 基 本 框 架, 只 是 没 有 填 写 实 际 的 内 容。 在 下 面 部 分, 我 们 将 具 体 开 发 一 个 十 分 简 单 的COM 服 务 程 序, 并 且 为 它 编 写 一 段 客 户 代 码 进 行 测 试, 使 大 家 对 使 用ATL 开 发COM 服 务 程 序 的 过 程 有 一 个 全 面 整 体 的 了 解。

    ---- 我 们 要 开 发 的 服 务 程 序 的 功 能 很 简 单, 它 只 实 现 一 个 接 口, 这 个 接 口 名 字 是ISimpleInterface, 接 口 只 有 一 个 成 员 函 数, 叫 做Welcome。 这 个 函 数 的 功 能 只 是 输 出 一 个"Hello World!" 的 字 符 串。

    ---- 按 照 上 一 部 分 介 绍 的 创 建COM 服 务 程 序 的 步 骤, 我 们 进 行 如 下 的 操 作。

    打 开Visual C++ 集 成 开 发 环 境。

    创 建 一 个 称 为SimpleTest 的ATL 工 程。

    在 这 个 工 程 中 插 入 新 的 对 象, 对 象 的 名 字 是SimpleInterface。

    设 置 接 口ISimpleInterface 的 有 关 属 性, 使 它 成 为 一 个 双 接 口。

    在 对 象 的 接 口ISimpleInterface 中 加 入 方 法Welcome。

    打 开ATL 加 入 的Welcome 方 法 的 框 架, 可 以 看 到 如 下 的 代 码 段。
    STDMETHODIMP CActiveXObject::get_TestProp(long *pVal)
    {
    AFX_MANAGE_STATE(AfxGetStaticModuleState())

    // TODO: Add your implementation code here

    return S_OK;
    }

    ---- 7 . 将 程 序 框 架 中 的 注 释 部 分 替 换 为 下 面 的 代 码:

    ::MessageBox(NULL,_T("Hello World!"),_T("Welcome"), MB_OK)。
    Welcome 方 法 被 调 用 时 将 弹 出 一 个 消 息 框。

    ---- 8 . 编 译 连 接 工 程。

    ---- 上 面 的 步 骤 完 成 以 后, 我 们 就 有 了 一 个 简 单 的COM 服 务 程 序, 而 且 已 经 被 注 册 到 当 前 系 统 中。

    ---- 下 面 我 们 要 完 成 一 个 简 单 的COM 客 户 程 序。 一 个COM 客 户 程 序 简 单 地 说 是 使 用COM 组 件 对 象 的 程 序。 客 户 程 序 调 用COM 对 象 的 基 本 流 程 如 下。

    创 建COM 对 象 的 实 例。 这 可 以 通 过 调 用Windows 系 统 的API 函 数CoCreateInstance 来 完 成。

    通 过 接 口 调 用 函 数。

    调 用IUnknown::Release 释 放COM 对 象 实 例。
    ---- 我 们 的 客 户 程 序 是 使 用MFC 编 写 的 一 个 基 于 对 话 框 的 简 单 应 用 程 序。 具 体 的 过 程 如 下:

    打 开Visual C++ 集 成 开 发 环 境。

    创 建 一 个 称 为SimpleClient 的 基 于 对 话 框 的MFC 工 程。

    在 对 话 框 中 加 入 一 个 按 钮, 名 字 为TEST。

    在SimpleClient.cpp 文 件 中 加 入 如 下 的 代 码。
    ---- (1) 在.cpp 文 件 #include "simpleclientdlg.h" 之 后 加 入 下 面 的 代 码:

    #include "d:/simpletest/simpletest_i.h"
    // 根 据 需 要 修 改 头 文 件 的 路 径

    ---- 加 入 的 头 文 件 是 在 编 译COM 服 务 程 序 过 程 中 自 动 生 成 的, 其 中 包 含 接 口 本 身 的 定 义、 接 口IID 的 定 义 和COM 对 象 的CLSID 的 定 义。 包 含 该 头 文 件 可 以 使 客 户 程 序 能 够 使 用COM 服 务 程 序。

    ---- (2) 在 按 钮TEST 的 消 息 控 制 函 数 中 加 入 如 下 的 代 码:

     
    HRESULT hr;
    ISimpleInterface* pIntf = NULL;
    hr = CoCreateInstance
    (CLSID_SimpleInterface, NULL, CLSCTX_SERVER ,
    IID_ISimpleInterface, (void **)& pIntf);
    if(SUCCEEDED(hr))
    {
    pIntf- >Welcome();
    pIntf- >Release();
    }

    ---- 上 面 的 代 码 首 先 通 过 系 统API CoCreateInstance 创 建COM 对 象, 得 到 接 口 的 指 针, 然 后 调 用 接 口 成 员 函 数Welcome, 最 后 通 过IUnknown::Release() 函 数 释 放COM 对 象 实 例。

    ---- 5 . 编 译 连 接 客 户 程 序。

    ---- 最 后, 我 们 可 以 测 试 客 户 程 序 是 否 正 常 运 行。 启 动 客 户 程 序, 当 单 击"TEST" 按 钮 时 我 们 可 以 看 到 弹 出 一 个 消 息 框, 这 正 是 我 们 的COM 服 务 程 序 提 供 的 功 能。




  • 2005-09-01

    COM的发展及其局限性 - [VC专栏]



    ---- 自 从1993 年Microsoft 首 次 公 布 了COM 技 术 以 后,Windows 平 台 上 的 开 发 模 式 发 生 了 巨 大 的 变 化, 以COM 为 基 础 的 一 系 列 软 件 组 件 化 技 术 将Windows 编 程 带 入 了 组 件 化 时 代。 广 大 开 发 人 员 在 为COM 带 来 的 软 件 组 件 化 趋 势 欢 欣 鼓 舞 的 同 时, 对 于COM 开 发 技 术 的 难 度 和 烦 琐 的 细 节 也 感 到 极 其 的 不 便。COM 编 程 一 度 被 视 为 一 种 高 不 可 攀 的 技 术, 令 人 望 而 却 步。 开 发 人 员 希 望 能 够 有 一 种 方 便 快 捷 的COM 开 发 工 具, 提 高 开 发 效 率, 更 好 地 利 用 这 项 技 术。

    ---- 针 对 这 种 情 况,Microsoft 公 司 在 推 出COM SDK 以 后, 为 简 化COM 编 程, 提 高 开 发 效 率, 采 取 了 许 多 方 案, 特 别 是 在MFC(Microsoft Foundation Class) 中 加 入 了 对COM 和OLE 的 支 持。 但 是 随 着Internet 的 发 展, 分 布 式 的 组 件 技 术 要 求COM 组 件 能 够 在 网 络 上 传 输, 而 又 尽 量 节 约 宝 贵 的 网 络 带 宽 资 源。 采 用MFC 开 发 的COM 组 件 由 于 种 种 限 制 不 能 很 好 地 满 足 这 种 需 求, 因 此Microsoft 在1995 年 又 推 出 了 一 种 全 新 的COM 开 发 工 具 — —ATL。

    ---- ATL 是ActiveX Template Library 的 缩 写, 它 是 一 套C++ 模 板 库。 使 用ATL 能 够 快 速 地 开 发 出 高 效、 简 洁 的 代 码, 同 时 对COM 组 件 的 开 发 提 供 最 大 限 度 的 代 码 自 动 生 成 以 及 可 视 化 支 持。 为 了 方 便 使 用, 从Microsoft Visual C++ 5.0 版 本 开 始,Microsoft 把ATL 集 成 到Visual C++ 开 发 环 境 中。1998 年9 月 推 出 的Visual Studio 6.0 集 成 了ATL 3.0 版 本。 目 前,ATL 已 经 成 为Microsoft 标 准 开 发 工 具 中 的 一 个 重 要 成 员, 日 益 受 到C++ 开 发 人 员 的 重 视。

    ---- ATL 究 竟 给 开 发 人 员 带 来 了 什 么 样 的 益 处 呢 ? 这 要 先 从ATL 产 生 以 前 的COM 开 发 方 式 说 起。

    ---- 在ATL 产 生 以 前, 开 发COM 组 件 的 方 法 主 要 有 两 种: 一 是 使 用COM SDK 直 接 开 发COM 组 件, 另 一 种 方 式 是 通 过MFC 提 供 的COM 支 持 来 实 现。

    ---- 直 接 使 用COM SDK 开 发COM 组 件 是 最 基 本 也 是 最 灵 活 的 方 式。 通 过 使 用Microsoft 提 供 的 开 发 包, 我 们 可 以 直 接 编 写COM 程 序。 但 是, 这 种 开 发 方 式 的 难 度 和 工 作 量 都 很 大, 一 方 面, 要 求 开 发 者 对 于COM 的 技 术 原 理 具 有 比 较 深 入 的 了 解( 虽 然 对 技 术 本 身 的 深 刻 理 解 对 使 用 任 何 一 种 工 具 都 是 非 常 有 益 的, 但 对 于COM 这 样 一 整 套 复 杂 的 技 术 而 言, 在 短 时 间 内 完 全 掌 握 是 很 难 的); 另 一 方 面, 直 接 使 用COM SDK 要 求 开 发 人 员 自 己 去 实 现COM 应 用 的 每 一 个 细 节, 完 成 大 量 的 重 复 性 工 作。 这 样 做 的 结 果 是, 不 仅 降 低 了 工 作 效 率, 同 时 也 使 开 发 人 员 不 得 不 把 许 多 精 力 投 入 到 与 应 用 需 求 本 身 无 关 的 技 术 细 节 中。 虽 然 这 种 开 发 方 式 对 于 某 些 特 殊 的 应 用 很 有 必 要, 但 这 种 编 程 方 式 并 不 符 合 组 件 化 程 序 设 计 方 法 所 倡 导 的 可 重 用 性, 因 此, 直 接 采 用COM SDK 不 是 一 种 理 想 的 开 发 方 式。

    ---- 使 用MFC 提 供 的COM 支 持 开 发COM 应 用 可 以 说 在 使 用COM SDK 基 础 上 提 高 了 自 动 化 程 度, 缩 短 了 开 发 时 间。MFC 采 用 面 向 对 象 的 方 式 将COM 的 基 本 功 能 封 装 在 若 干MFC 的C++ 类 中, 开 发 者 通 过 继 承 这 些 类 得 到COM 支 持 功 能。 为 了 使 派 生 类 方 便 地 获 得COM 对 象 的 各 种 特 性,MFC 中 有 许 多 预 定 义 宏, 这 些 宏 的 功 能 主 要 是 实 现COM 接 口 的 定 义 和 对 象 的 注 册 等 通 常 在COM 对 象 中 要 用 到 的 功 能。 开 发 者 可 以 使 用 这 些 宏 来 定 制COM 对 象 的 特 性。

    ---- 另 外, 在MFC 中 还 提 供 对Automation 和ActiveX Control 的 支 持, 对 于 这 两 个 方 面,Visual C++ 也 提 供 了 相 应 的AppWizard 和ClassWizard 支 持, 这 种 可 视 化 的 工 具 更 加 方 便 了COM 应 用 的 开 发。

    ---- MFC 对COM 和OLE 的 支 持 确 实 比 手 工 编 写COM 程 序 有 了 很 大 的 进 步。 但 是MFC 对COM 的 支 持 还 不 够 完 善 和 彻 底, 例 如 对COM 接 口 定 义 的IDL 语 言,MFC 并 没 有 任 何 支 持, 此 外 对 于 近 些 年 来COM 和ActiveX 技 术 的 新 发 展MFC 也 没 有 提 供 灵 活 的 支 持。 这 是 由MFC 设 计 的 基 本 出 发 点 决 定 的。MFC 被 设 计 成 对Windows 平 台 编 程 开 发 的 面 向 对 象 的 封 装, 自 然 要 涉 及Windows 编 程 的 方 方 面 面,COM 作 为Windows 平 台 编 程 开 发 的 一 个 部 分 也 得 到MFC
  • 2005-09-01

    ATL基本技术 - [VC专栏]



    ---- 虽 然 使 用ATL 开 发COM 应 用 是 一 件 非 常 简 单 的 事 情, 但 是 在ATL 简 单 易 用 的 界 面 后 面 却 包 含 着 复 杂 的 技 术。 面 对ATL 生 成 的 大 量 代 码, 我 们 即 使 不 去 深 入 地 了 解 这 些 代 码 的 含 义 也 可 以 开 发 出COM 应 用 来, 但 是 如 果 我 们 要 充 分 地 挖 掘ATL 的 潜 力, 开 发 出 更 灵 活、 强 大 的COM 应 用, 则 必 须 对ATL 使 用 的 基 本 技 术 有 所 了 解。 研 究ATL 的 实 质 最 好 的 教 材 就 是 由Visual C++ 提 供 的ATL 源 代 码。 本 文 这 一 部 分 只 是 对ATL 中 用 到 的 最 基 本 的 技 术 进 行 简 单 的 介 绍。

    ---- 简 单 地 说 来,ATL 中 所 使 用 的 基 本 技 术 包 括 以 下 几 个 方 面:

    COM 技 术

    C++ 模 板 类 技 术(Template)

    C++ 多 继 承 技 术(Multi-Inheritance)
    ---- COM 技 术 是 理 解ATL 的 基 础, 使 用ATL 进 行 开 发 要 对COM 技 术 的 基 本 概 念 有 最 低 限 度 的 了 解。 由 于COM 是 一 项 非 常 复 杂 庞 大 的 技 术 体 系, 限 于 本 文 的 篇 幅, 这 里 不 再 赘 述。 对 于 本 文 中 提 到 的COM 基 本 概 念 也 不 做 过 多 的 解 释, 请 读 者 参 阅 有 关 的 参 考 书 籍。 作 为ATL 最 核 心 的 实 现 技 术 的 模 板 是 对 标 准C++ 语 言 的 扩 展, 但 是 在 大 多 数 的C++ 编 程 环 境 中, 人 们 很 少 使 用 它, 这 是 因 为 模 板 的 功 能 虽 然 很 强, 但 是 它 内 部 机 制 比 较 复 杂, 需 要 比 较 多 的C++ 知 识 和 经 验 才 能 灵 活 地 使 用 它。 在MFC 中 的CObjectArray 等 功 能 类 就 是 由 模 板 来 定 义 的。 完 全 通 过 模 板 来 定 义 程 序 的 整 体 类 结 构,ATL 是 迄 今 为 止 做 得 最 为 成 功 的。

    ---- 所 谓 模 板 类 简 单 地 说 是 对 类 的 抽 象。 我 们 知 道C++ 语 言 用 类 定 义 了 构 造 对 象( 这 里 指C++ 对 象 而 不 是COM 对 象) 的 方 式, 对 象 是 类 的 实 例, 而 模 板 类 定 义 的 是 类 的 构 造 方 式, 使 用 模 板 类 定 义 实 例 化 的 结 果 产 生 的 是 不 同 的 类。 因 此 可 以 说 模 板 类 是“ 类 的 类”。

    ---- 在C++ 语 言 中 模 板 类 的 定 义 格 式 如 下:

    template < class T >
    class MyTemp
    {
    MyTemp< T >( ) { };
    ~MyTemp< T >( ) { };
    int MyFunc( int a) ;
    }
    … …
    Int MyTemp< T >::MyFunc( int a)
    {
    }

    ---- 首 先 使 用C++ 的 关 键 字“template” 来 声 明 一 个 模 板 类 的 定 义。 在 关 键 字 后 面 是 用 尖 括 号 括 起 来 的 类 型 参 数。 正 是 根 据 这 个 类 型 参 数, 编 译 器 才 能 在 编 译 过 程 中 将 模 板 类 的 具 体 定 义 转 化 为 一 个 实 际 的 类 的 定 义, 即 生 成 一 个 新 类。 接 下 来 的 定 义 方 式 与 普 通 的 类 定 义 十 分 相 似, 只 是 在 类 的 函 数 定 义 中 都 要 带 有 类 型 参 数 的 说 明。

    ---- 下 面 的 程 序 段 说 明 了 模 板 类 的 用 法:

    typedef MyTemp< MyClass > myclassfromtemp;
    myclassfromtemp m;
    int a = m.Myfunc(10);

    ---- 通 常 在 使 用 模 板 类 时 为 了 方 便 起 见, 使 用 一 个 关 键 字“typedef” 为 新 定 义 出 来 的 类 取 一 个 名 字。 在 上 面 的 程 序 段 中 假 设“MyClass” 是 一 个 由 用 户 定 义 的 类, 通 过 将 这 个 类 的 名 字 作 为 类 型 参 数 传 递 给 模 板 类, 我 们 可 以 创 建 一 个 新 的 类, 这 个 类 的 行 为 将 以 模 板 类 的 定 义 为 基 础, 例 如 它 具 有 模 板 类 定 义 的 所 有 成 员 函 数, 同 时 这 个 类 又 是 对 模 板 类 行 为 的 一 种 修 改, 这 种 修 改 是 通 过 用 户 提 供 的 类 型 参 数 来 实 现 的。 赋 予 模 板 类 以 不 同 的 类 型 参 数, 则 得 到 行 为 框 架 相 似 但 具 体 行 为 不 同 的 一 组 类 的 集 合。 有 了 新 的 类 的 定 义 以 后, 我 们 可 以 像 使 用 普 通 类 一 样 来 创 建 一 个 类 的 实 例, 即 一 个 新 的 对 象, 并 且 调 用 这 个 对 象 的 成 员 函 数。

    ---- 模 板 类 是 对 标 准C++ 语 言 的 最 新 扩 展, 虽 然 它 的 功 能 很 强 大, 但 是 要 想 使 用 好 模 板 类 需 要 相 当 多 的 关 于 语 言 和 编 程 的 经 验 和 知 识, 而 且 错 误 地 使 用 模 板 类 又 会 对 程 序 的 结 构 和 运 行 效 率 带 来 很 大 的 副 作 用, 因 此 一 般 的 编 程 环 境 和 编 程 书 籍 对 模 板 类 的 使 用 都 采 取 谨 慎 的 态 度。 而ATL 的 核 心 就 是 由 几 十 个 模 板 类 构 成 的, 通 过 研 究ATL 的 源 代 码 可 以 使 我 们 对 模 板 类 的 使 用 有 比 较 全 面 的 认 识。

    ---- 多 继 承 技 术 同 模 板 一 样, 是C++ 语 言 中 极 具 争 议 性 的 技 术。 使 用 多 继 承 技 术 可 以 使 程 序 的 设 计 和 实 现 更 加 灵 活, 但 是, 由 于 多 继 承 的 复 杂 性 和 自 身 概 念
  • 2005-09-01

    ATL的基本目标 - [VC专栏]

    解 决 上 述COM 开 发 方 法 中 的 问 题 正 是ATL 的 基 本 目 标。

    ---- 首 先,ATL 的 基 本 目 标 就 是 使COM 应 用 开 发 尽 可 能 地 自 动 化, 这 个 基 本 目 标 就 决 定 了ATL 只 面 向COM 开 发 提 供 支 持。 目 标 的 明 确 使ATL 对COM 技 术 的 支 持 达 到 淋 漓 尽 致 的 地 步。 对COM 开 发 的 任 何 一 个 环 节 和 过 程,ATL 都 提 供 支 持, 并 将 与COM 开 发 相 关 的 众 多 工 具 集 成 到 一 个 统 一 的 编 程 环 境 中。 对 于COM/ActiveX 的 各 种 应 用,ATL 也 都 提 供 了 完 善 的Wizard 支 持。 所 有 这 些 都 极 大 地 方 便 了 开 发 者 的 使 用, 使 开 发 者 能 够 把 注 意 力 集 中 在 与 应 用 本 身 相 关 的 逻 辑 上。

    ---- 其 次,ATL 因 其 采 用 了 特 定 的 基 本 实 现 技 术, 摆 脱 了 大 量 冗 余 代 码, 使 用ATL 开 发 出 来 的COM 应 用 的 代 码 简 练 高 效, 即 所 谓 的“ 瘦 代 码”(Slim Code)。ATL 在 实 现 上 尽 可 能 采 用 优 化 技 术, 甚 至 在 其 内 部 提 供 了 所 有C/C++ 开 发 的 程 序 所 必 须 具 有 的C 启 动 代 码 的 替 代 部 分。 同 时ATL 产 生 的 代 码 在 运 行 时 不 需 要 依 赖 于 类 似MFC 程 序 所 需 要 的 庞 大 的 代 码 模 块, 包 含 在 最 终 模 块 中 的 功 能 是 用 户 认 为 最 基 本 和 最 必 需 的。 这 些 措 施 使 采 用ATL 开 发 的COM 组 件( 包 括ActiveX Control) 可 以 在 网 络 环 境 下 实 现 应 用 的 分 布 式 组 件 结 构。

    ---- 第 三,ATL 的 各 个 版 本 对Microsoft 的 基 于COM 的 各 种 新 的 组 件 技 术 如MTS、ASP 等 都 有 很 好 的 支 持,ATL 对 新 技 术 的 反 应 速 度 大 大 快 于MFC。ATL 已 经 成 为Microsoft 支 持COM 应 用 开 发 的 主 要 开 发 工 具, 因 此COM 技 术 方 面 的 新 进 展 在 很 短 的 时 间 内 都 会 在ATL 中 得 到 反 映。 这 使 开 发 者 使 用ATL 进 行COM 编 程 可 以 得 到 与 直 接 使 用COM SDK 编 程 同 样 的 灵 活 性 和 强 大 的 功 能。

    ---- 本 文 的 目 的 就 是 希 望 在 有 限 的 篇 幅 中 能 够 使 读 者 对ATL 的 使 用 和 基 本 原 理 有 一 个 初 步 的 了 解, 为 广 大 的COM 开 发 人 员 更 好 地 使 用ATL 开 发 起 到 抛 砖 引 玉 的 作 用。
  • 2005-09-01

    Visual C ++经验谈 - [VC专栏]

    Visual C ++经验谈

      学习VC是一个艰难的过程。如果在学习的过程中遇到一些经验人士为你指点一二,那么一切都变的那么简单,这里我收录了一些在学习使用VC过程中积累的经验,以期与大家共同进步。
    1、取得系统时间
    方法:
    SYSTEMTIME systime;
    ::GetSystemTime(&systime);
    CTime time(systime);2、在程序中添加ODBC数据源
    方法:使用SQLConfigDataSource函数。例如:
    SQLConfigDataSource(NULL,ODBC_ADD_DSN,
    (LPSTR)"SQL Server",
    (LPSTR)"DSN=medicine1998/0"
    "SERVER=DEC/0"
    "DATABASE=medicine1998/0"))
    //添加一个ODBC数据源,其类型为
    //SQL Server,服务器为DEC,名字为medicine1998,
    //数据库为medicine19983、在Visual C++中使用DBGrid控件的方法
    (1)、插入一个MicrosoftRemoteData控件;
    (2)、设定其DataSource为所需要的ODBC数据源;
    (3)、设定用户名和密码;
    (4)、写入SQL查询语句;
    (5)、插入一个DBGrid控件;
    (6)、设定为绑定方式;
    (7)、设定其绑定的数据源为前面插入的Microsoft-RemoteData控件的ID;
    (8)、由于只能修改前两列的列头显示(至少我不知道如何去修改第3列),所以为了重新设定每一列的列头显示,同时也是为了指定显示的列,应该修改前面MicrosoftRemoteData控件中的查询语句,指定获取列和更改列名,例如:select name as 姓名,phone as 电话 from address。此语句就是从表address中选取name和phone两列,并指定了显示的列名为“姓名”和“电话”。
    4、在ODBC编程中,在过滤器中可以用参数取代过滤字符串,以便在运行时动态改变过滤器,但是该参数必须用如下方法声明:
    (1)在记录集的定义中添加成员参数:
    class CStudentSet : public CRecordset
    {
    // Field/Param Data
    //{{AFX_FIELD(CStudentSet, CRecordset)
    CString m_strFirstName;
    CString m_strLastName;
    CString m_strStudentID;
    CString m_strGradYear;
    //}}AFX_FIELD
    CString m_strGradYrParam; //成员参数
    };
    (2)改变在CPP文件中的DoFieldExchange成员函数,并且对每一个你添加在类中的成员参数都调用一次RFX函数,如下:
    pFX->SetFieldType( CFieldExchange::param );
    //指示以下给出的是参数绑定
    // RFX calls for parameter data members
    //在此处加入RFX调用:
    pFX->RFX_Text(pFX,"bookname", m_strGradYrParam);
    其中,bookname是要在其上添加参数的列名,后面是参数名。
    (3)在你的recordset类的构建函数中,增加反映参数个数的m_nParams成员变量的值。
    (4)然后可以在你的SQL过滤串中以“?”代替可变过滤参数了,这种对应是一一对应的,即“?”的顺序要严格遵守RFX调用的顺序。然后给出过滤参数的值,就可以用此值代替“?”了。注意,该过滤参数的值一定要在数据源打开之前给定



  • 2005-09-01

    VC中实现在线版本检测 - [VC专栏]


    VC中实现在线版本检测
    ________________________________________

    发表日期:2004年12月7日 【编辑录入:webmaster】



    现在很多共享软件都有"在线升级"功能,比如"Windows 优化大师","超级兔子",所谓在线升级就是一个版本检测程序,通过他,用户可以随时检查有没有新版本程序,以便及时升级,下面我们就在自己的程序里来实现这个功能:
    在正式编程前,我要说的是:我不赞成某些共享软件在每次启动时候都检测新版本,并弹出窗口提醒用户,有时候这大大影响或者妨碍了用户的操作和使用,我们要实现的功能是:让用户自己去检查新版本!
    编程思路是这样的:首先我们在主页某个地址放一个文件,然后在我们的程序里来检查它,以便获得新版本信息!这里版本信息文件我们用INI文件来操作!比如下列格式,
    [Version]
    Version=12
    [URL]
    URL=http://www.123.com/123.exe
    其中的 Version是新版本号,12表示V1.2版,这个格式完全可以自己设置,URL是新版本程序下载地址
    另外,要获取INTETNET上的文件,我们一般有2种办法,一是通过FTP下载,二是HTTP直接连接,如果你有国际域名空间,那么就可以使用匿名FTP在程序里获取版本信息文件,但是对于使用免费主页的朋友来说就麻烦了。因为服务器不是我们的,这就不太方便了。那么采用HTTP方式获取版本信息文件就很有必要了!要获取网站上的某个文件并保存在本地,我们只需要一个API函数URLDownloadToFile()即可,当成功下载INI文件后我们就可以读取这个文件并进行分析工作了!
    具体实现过程:
      打开VC,建立MFC程序,放置一个标签IDC_VER ,用来输出信息,另外安排2个按狃,分别是IDC_CHECK,IDC_DOWNLOAD
      前者用来检查是否有新版本,后者用来下载新版本程序!
      首先我们添加一个成员变量char new_url[100] 来存放新版本程序的下载地址:
      然后在IDC_CHECK 按钮事件里输入以下代码来检查有无新版本:
    SetDLgItemText(IDC_VER,"正在检查,请等待。。。。。。");
    int ret=URLDownloadToFile(NULL,"http://ip/vesion.ini","c:version.ini",0,NULL);
    if (ret==S_OK) //如果下载成功
    {
    // 读取Version 段的数据,得到新版本好
    int newversion=GetPrivateProfileInt("Version","Ver",10,"c:version.ini");
    if (newversion>10) //跟当前版本比较,10表示目前版本V1.0 ,根据具体版本自己设置
    {
    SetDLgItemText(IDC_VER,"有新版本了!");
    // 得到新版本下载地址给变量new_url
    GetPrivateProfileString("URL","URL",0,new_url,100,"c:version.ini");
    }
    else
    SetDLgItemText(IDC_VER,"抱歉。还没有新版本!");

    DeleteFile("c:version.ini"); //用完后删除
    }
    else
    SetDLgItemText(IDC_VER,"网络连接失败!");
    在IDC_DOWNLOAD 按钮事件里输入以下代码以便打开IE浏览器下载新文件:
    ShellExecute(this->m_hWnd,"open",new_url,NULL,"c:",SW_SHOW);
    现在我们编辑一个VERSION.INI文件,并放到主页里,然后就可以实现新版本检查功能了!
      本文只是讲述一个编程思路,具体功能还要各位编程爱好者自己去完善,比如:如果你想让程序自动升级版本,那么你需要把这个版本检查程序做成一个单独的EXE文件,通过它来检查新版本,并从服务器上下载新版本来覆盖旧版本!这些功能,大家就根据自己的需要去实现吧!


  • 2005-09-01

    打开光驱和关闭光驱的函数 - [VC专栏]

    void CControlDlg::OnOpencdrom() //打开CD-ROM
    {
    // TODO: Add your control notification handler code here

    mciSendString("Set cdAudio door open wait",NULL,0,NULL);

    }

    void CControlDlg::OnClosecdrom() //关闭CD-ROM
    {
    // TODO: Add your control notification handler code here

    mciSendString("Set cdAudio door closed wait",NULL,0,NULL);

    }
  • 2005-09-01

    五种查询Internet连接状态的方法 - [VC专栏]

    简介: 1.Powersock 控件法:
    这种方法最简单,利用FastNet页的 Powersock控件的LocalIP属性即可判断:
    if(Powersock1->LocalIP=="127.0.0.1"):在线
    else:离线
    特点:[1]判断连接状态,[2]获得本地IP。

    2.使用URL.DLL的InetIsOffline(0) 函数:
    Win2K:URL.DLL存放在SYSTEM32;
    Win9x:URL.DLL存放在SYSTEM;
    用GetSystemDirectory(...)得到系统目录。
    InetIsOffline(0)返回值:
    TRUE: 离线; FALSE:在线。
    特点:判断连接状态。

    3.WinSock编程法:见程序
    特点:[1]判断连接状态;[2]获得本地IP和主机名。

    4.WinInet.DLL的InternetGetConnectedState(&dwFlag,0)函数:
    注意:为使用该函数,须在项目文件中加入:USELIB("WinInet.LIB")
    特点:获得较详的连接描述!

    5.RASAPI32.DLL的RasEnumConnections函数:
    要使用该“枚举所有活动连接”函数,必须:
    #include "ras.h"。

    若连接数>0:本机当前已连入Internet;
    否则: 本机当前未连入Internet;

    源码如下,在[BCB5 + WIN2K + 拨号上网]下通过(N字头的为菜单项):

    -------------Powersock控件法-----------------------------------------
    void __fastcall TForm1::N11Click(TObject *Sender)
    {
    if(Powersock1->LocalIP=="127.0.0.1")
    ShowMessage("未连接:"+Powersock1->LocalIP);
    else ShowMessage("已连接:"+Powersock1->LocalIP);
    }


    -------------URL.DLL的InetIsOffline函数法----------------------------
    HINSTANCE hDLL;
    typedef bool __stdcall(*FUN)(int); 定义DLL函数指针FUN
    FUN isOffLine;
    void __fastcall TForm1::N21Click(TObject *Sender)
    {
    char Buffer[MAX_PATH];
    GetSystemDirectory(Buffer,MAX_PATH);
    hDLL=LoadLibrary((AnsiString(Buffer)+"/URL.DLL").c_str());
    if(hDLL==NULL){ ShowMessage("Cannot load URL.DLL! Return... "); return; }
    isOffLine=(FUN)GetProcAddress(hDLL,"InetIsOffline");
    if(isOffLine==NULL){ ShowMessage("Cannot load InetIsOffline(int), Return..."); return; }
    if(!isOffLine(0)) ShowMessage("已连接");
    else ShowMessage("未连接");
    FreeLibrary(hDLL);
    }


    ------------WinSock法------------------------------------------------
    void __fastcall TForm1::N31Click(TObject *Sender)
    {
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested=MAKEWORD(1,1); Start up WinSock
    WSAStartup(wVersionRequested,&wsaData);
    -----------------------------------------
    hostent *p; char *p2; char s[128];
    gethostname(s,128); Get the computer name
    p=gethostbyname(s);
    p2=inet_ntoa(*((in_addr *)p->h_addr)); Get the IpAddress
    -----------------------------------------
    AnsiString LocationIP=p2;
    if(LocationIP=="127.0.0.1")
    ShowMessage("未连接:"+LocationIP);
    else ShowMessage("已连接:"+LocationIP);
    WSACleanup();
    }


    -----------WinInet.DLL的InternetGetConnectedState函数法----------------
    void __fastcall TForm1::N41Click(TObject *Sender)
    {
    StaticText1->Caption=""; StaticText2->Caption=""; StaticText3->Caption="";
    StaticText4->Caption=""; StaticText5->Caption=""; StaticText6->Caption="";
    StaticText7->Caption="";
    DWORD dwFlag;
    InternetGetConnectedState(&dwFlag,0);
    if(dwFlag & INTERNET_CONNECTION_MODEM) StaticText1->Caption="Yes"; MODEM连接
    else StaticText1->Caption="No";
    if(dwFlag & INTERNET_CONNECTION_LAN) StaticText2->Caption="Yes"; LAN连接
    else StaticText2->Caption="No";
    if(dwFlag & INTERNET_CONNECTION_PROXY) StaticText3->Caption="Yes"; 代理连接
    else StaticText3->Caption="No";
    ---------检查是否连接-------------------------------------------
    if(InternetGetConnectedState(NULL,0)) StaticText4->Caption="Yes"; 在线
    else StaticText4->Caption="No";
    if(dwFlag & INTERNET_CONNECTION_OFFLINE) StaticText5->Caption="Yes";//离线。注:不好用!
    else StaticText5->Caption="No";
    ----------------------------------------------------------------
    if(dwFlag & INTERNET_RAS_INSTALLED) StaticText6->Caption="Yes";
    else StaticText6->Caption="No";
    if(dwFlag & INTERNET_CONNECTION_CONFIGURED) StaticText7->Caption="Yes";
    else StaticText7->Caption="No";
    }
    ----------RASAPI32.DLL的RasEnumConnections函数法---------------------------
    #include "ras.h"
    void __fastcall TForm1::N51Click(TObject *Sender)
    {
    RASCONN RASconn[256]; 活动连接数组
    DWORD BuffSize; 数组所占内存大小;
    DWORD ConnNum; 活动连接数目
    RASconn[0].dwSize=sizeof(RASCONN); 必须指定一个连接[数组元素]的内存大小;
    BuffSize=sizeof(RASCONN)*256;
    DWORD
  • 2005-09-01

    显示字符串 - [VC专栏]

    void CTextOutView::OnMessageMessage3()
    {
    m_bMessage1=false;
    m_bMessage2=false;
    m_bMessage3=true;
    CClientDC dc(this);
    //显示字符串
    dc.TextOut(100,500,"hello3,hello");
    }

    //为菜单项添加核对标记
    void CTextoutView::OnUpdateMessageMessage1(CCmdUI* pCmdUI)
    {
    pCmdUI->SetCheck(m_bMessage1);
    }

    void CTextoutView::OnUpdateMessageMessage2(CCmdUI* pCmdUI)
    {
    pCmdUI->SetCheck(m_bMessage2);
    }

    void CTextoutView::OnUpdateMessageMessage3(CCmdUI* pCmdUI)
    {
    pCmdUI->SetCheck(m_bMessage3);
    }
  • 2005-09-01

    修改窗口标题栏 - [VC专栏]

    在缺省情况下,窗口标题栏中显示的文档名为文件名。若要在标题栏显示一个长字符串,而又不修改文件名,则可将项目工作区转换到 Resource View面版,选择串表( StringTable)资源,在StringTable中双击 IDR-MAIN-FRAME项,caption中显示一字符串xx/n/yy......,将第一个参数修改为用户自己希望见到的主窗口标题即可。
  • 2005-09-01

    修改主框架窗口、子窗口及其显示性质 - [VC专栏]

    可通过覆盖CWnd的成员函数PreCreateWindow来修改主窗口和子窗口。PreCreateWindow函数在即将创建窗口前被调用,函数原型为:

    Virtual BOOL PreCreateWindow(CREATESTRUCT cs)

    如果要覆盖 PreCreateWindow函数,则在创建窗口前可以修改 CREATESTRUCT结构以替换缺省参数。CREATESTRUCT结构存放窗口特征,如窗口坐标、风格等,还可以定义新窗口风格,
    若想修改主框架窗口,则可以在MainFrm.cpp的下列成员函数中加入待修改的内容。例如:

    BOOL CmainFrame::PreCreateWindow(CREATESTRUCT&cs)
    {
    //通过修改CREATESTRUCT结构来修改窗口类或风格
    //定义新窗口的高度、宽度
    cs.cx=450;
    cs.cy=300;
    //定义新窗口风格为去掉主窗口名及最大化等按钮
    cs.style=ws-POPWINDO;
    return CframeWnd::PreCreateWindow(cs);
    }

    定制子窗口的操作与上述主窗口相同,可在 ChildFrm.cpp中加入以下内容:
    BOOL CmainFrame::PreCreateWindow(CREATESTRUCT&cs)
    {
    //通过修改CREATESTRUCT结构来修改窗口类或风格
    return C mdichildWnd::PreCreateWindow(cs);
    }
    要修改视图窗口的显示性质,则可在视图文件 xxView.cpp的下述成员函数中加入以下语句:
    BOOL xxView::PreCreateWindow(CREATESTRUCT&cs)
    {
    //增加的语句
    cs.lpszClass=AfxRegisterWndClass(cs-HREDRAW|CS-VREDRAW,0,(HBRUSH)::GetStockObject(WHITE-BRUSH),0);
    return CscrollView::PreCreateWindow(cs);
    }
    其中, cs的参数pszClass用于存放Windows窗口类名称。要想注册Windows 窗口类,则必须调用全局函数AfxRegisterWndClass。该函数原型为:
    LPCTSTR AFXAPI AfxRegisterWndClass(UINTnClassStyle,HCURSOR hCursor=0,HBRUSH hbrBackground=0,HICON hIcon=0)
    上述各参数用于定义风格,其含义分别为光标资源句柄、背景资源句柄、图标资源句柄。上述增加的语句的作用是:改变窗口大小时重画窗口、不显示光标图标、设置白色背景。
  • 2005-09-01

    虚拟键 - [VC专栏]

    符号常量 十六进制值 指定的鼠标或键盘按键
      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’
       1键:’1’
      //////////////////////////////////////////////

      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 键
  • 2005-09-01

    用VC++6.0编写Proxy服务器 - [VC专栏]

    用VC++6.0编写Proxy服务器

    我们一般常用的Internet代理服务器是用微软的Proxy Server 2.0 。但我们可以自己动手编写一个简单、小型的Proxy Server 。下面介绍具体的实现方法。
    一. 原理

    本程序的结构原理如下: 对于每一个用户的请求(Internet 请求,由浏览器发出),本程序将启动两个线程,一个把本地用户的请求数据发送到远程的Internet主机,另一个线程把远程主机的回应数据发送到本地请求用户。

    二. 主要函数

    UserToProxyThread ( void * pParam ):

    它是用来把本地用户请求数据发送到远程主机的,起服务器线程角色。当接到本地(局域网)用户的请求,它就启动另一个自身线程,以侦听别的用户的请求,并读出已接收到的请求数据,接着启动第二个线程ProxyToServer()(这个线程用来连接远程主机),当远程主机连接成功后,它把已读出的本地用户请求数据发送到远程主机。

    ProxyToServer ( void * pParam):

    可以被当作是客户端服务,它把远程主机发送来的数据分发给本地请求用户。

    三. 开发运行环境

    本程序是在VC++6.0环境下开发的,在Win95 和 WinNT4.0下运行正常。

    四. 详细代码

    #include "stdafx.h"#include "Proxy.h"
    #include < winsock2.h > //WINSOCKET API 2。0#include < stdlib.h >
    #include < stdio.h >
    #include < string.h >
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
    ////////////////////////////////////////////////////////////////
    #define HTTP "http://"
    #define FTP "ftp://"
    #define PROXYPORT 5001 //Proxy 端口#define BUFSIZE 10240 //缓冲区大小CWinApp theApp;using namespace std;
    UINT ProxyToServer(LPVOID pParam);
    UINT UserToProxyThread(void *pParam);
    struct SocketPair
    {
    SOCKET user_proxy; //socket : 本地机器到PROXY 服务机
    SOCKET proxy_server; //socket : PROXY 服务机到远程主机
    BOOL IsUser_ProxyClosed; // 本地机器到PROXY 服务机状态
    BOOL IsProxy_ServerClosed; // PROXY 服务机到远程主机状态
    };
    struct ProxyParam
    {
    char Address[256]; // 远程主机地址
    HANDLE User_SvrOK; // PROXY 服务机到远程主机的联结状态
    SocketPair *pPair; // 维护一组SOCKET的指针
    int Port; // 用来联结远程主机的端口
    }; //这个结构用来PROXY SERVER与远程主机的信息交换.
    SOCKET gListen_Socket; //用来侦听的SOCKET。
    int StartServer() //启动服务
    {
    WSADATA wsaData;
    sockaddr_in local;
    SOCKET listen_socket;
    if(::WSAStartup(0x202,&wsaData)!=0)
    {
    printf("/nError in Startup session./n");
    WSACleanup();
    return -1;
    }
    local.sin_family=AF_INET;
    local.sin_addr.s_addr=INADDR_ANY;
    local.sin_port=htons(PROXYPORT);
    listen_socket=socket(AF_INET,SOCK_STREAM,0);
    if(listen_socket==INVALID_SOCKET)
    {
    printf("/nError in New a Socket.");
    WSACleanup();
    return -2;
    }
    if(::bind(listen_socket,(sockaddr *)&local,sizeof(local))!=0)
    {
    printf("/n Error in Binding socket.");
    WSACleanup();
    return -3;
    }
    if(::listen(listen_socket,5)!=0)
    {
    printf("/n Error in Listen.");
    WSACleanup();
    return -4;
    }
    gListen_Socket=listen_socket;
    AfxBeginThread(UserToProxyThread,NULL); //启动侦听return 1;}
    int CloseServer() //关闭服务{closesocket(gListen_Socket);
    WSACleanup();
    return 1;
    }
    //分析接收到的字符,得到远程主机地址
    int GetAddressAndPort( char * str, char *address, int * port)
    {
    char buf[BUFSIZE], command[512], proto[128], *p;
    int j;
    sscanf(str,"%s%s%s",command,buf,proto);
    p=strstr(buf,HTTP);
    //HTTP
    if(p)
    {
    p+=strlen(HTTP);
    for(int i=0;i< strlen(p);i++)
    if( *(p+i)==`/`) break;
    *(p+i)=0;
    strcpy(address,p);
    p=strstr(str,HTTP);
    for(int j=0;j< i+strlen(HTTP);j++)
    *(p+j)=` `; //去掉远程主机%C




  • 2005-09-01

    使用MFC快速实现网络编程 - [VC专栏]

    使用MFC快速实现网络编程

    福州大学
    王骏

    随着计算机网络化的深入,计算机网络编程在程序设计的过程中变得日益重要。
    由于C++语言对底层操作的优越性,许多文章都曾经介绍过用VC++进行Socket编
    程的方法。但由于都是直接利用动态连接库wsock32.dll进行操作,实现比较繁琐。
    其实,VC++的MFC类库中提供了CAsyncSocket这样一个套接字类,用他来实现
    Socket编程,是非常方便的。

    本文将用一个Echo例程来介绍CAsyncSocket类的用法。

    一. 客户端

    1. 创建一个Dialog Based项目:CSockClient。

    2. 设计对话框

    去掉Ok和Cancle两个按钮,增加ID_Connect(连接)、ID_Send(发送)、
    ID_Exit(关闭)按钮,增加ListBox控件IDC_LISTMSG和Edit控件IDC_EDITMSG,
    并按下表在ClassWizard中为CCSockClientDlg类添加变量。

    Control ID Type Member

    IDC_EDITMSG CEdit m_MSG
    IDC_LISTMSG ClistBox m_MSGS

    3. CAsyncSocket类用DoCallBack函数处理MFC消息,当一个网络事件发生时,
    DoCallBack函数按网络事件类型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT
    分别调用OnReceive、OnSend、OnAccept、OnConnect函数。由于MFC把这些事件处
    理函数定义为虚函数,所以要生成一个新的C++类,以重载这些函数,做法如下:

    以Public方式继承CAsyncSocket类,生成新类MySock;

    为MySock类添加虚函数OnReceive、OnConnect、OnSend

    4. 在MySock.ccp中添加以下代码

    #include "CSockClient.h"
    #include "CSockClientDlg.h"

    5. 在MySock.h中添加以下代码

    public:
    BOOL m_bConnected; //是否连接
    UINT m_nLength; //消息长度
    char m_szBuffer[4096]; //消息缓冲区

    6. 在MySock.ccp中重载各函数

    MySock::MySock()
    {
    m_nLength=0;
    memset(m_szBuffer,0,sizeof(m_szBuffer));
    m_bConnected=FALSE;
    }

    MySock::~MySock()
    {
    //关闭套接字
    if(m_hSocket!=INVALID_SOCKET)
    Close();
    }

    void MySock::OnReceive(int nErrorCode)
    {
    m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0);
    //下面两行代码用来获取对话框指针
    CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp();
    CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd;
    pDlg- >m_MSGS.InsertString(0,m_szBuffer);
    memset(m_szBuffer,0,sizeof(m_szBuffer));
    CAsyncSocket::OnReceive(nErrorCode);
    }

    void MySock::OnSend(int nErrorCode)
    {
    Send(m_szBuffer,m_nLength,0);
    m_nLength=0;
    memset(m_szBuffer,0,sizeof(m_szBuffer));
    //继续提请一个“读”的网络事件,接收Server消息
    AsyncSelect(FD_READ);
    CAsyncSocket::OnSend(nErrorCode);
    }

    void MySock::OnConnect(int nErrorCode)
    {
    if (nErrorCode==0)
    {
    m_bConnected=TRUE;
    CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp();
    CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd;
    memcpy(m_szBuffer,"Connected to ",13);
    strncat(m_szBuffer,pDlg- >m_szServerAdr,
    sizeof(pDlg- >m_szServerAdr));
    pDlg- >m_MSGS.InsertString(0,m_szBuffer);
    AsyncSelect(FD_READ); ////提请一个“读”的网络事件,准备接收
    }
    CAsyncSocket::OnConnect(nErrorCode);
    }

    7. 新建对话框IDD_Addr,用来输入IP地址和Port;生成新类CAddrDlg。增加两个
    Edit控件:IDC_Addr、IDC_Port按下表在ClassWizard中为CAddrDlg类添加变量。

    Control ID Type Member

    IDC_Addr CString m_Addr
    IDC_Port Int m_Port

    8. 在CSockClientDlg.ccp中添加代码

    #include "AddrDlg.h"
    protected:
    int TryCount;
    MySock m_clientSocket;
    UINT m_szPort;
    public:
    char m_szServerAdr[256];

    9. 双击IDD_CSOCKCLIENT_DIALOG对话框中的“连接”按钮,添加以下代码

    void CCSockClientDlg::OnConnect()
    {
    m_clientSocket.ShutDown(2);
    m_clientSocket.m_hSocket=INVALID_SOCKET;
    m_clientSocket.m_bConnected=FALSE;
    CAddrDlg m_Dlg;
    //默认端口1088
    m_Dlg.m_Port=1088;
    if (m_Dlg.DoModal()==IDOK && !m_Dlg.m_Addr.IsEmpty())
    {
    memcpy(m_szServerAdr,m_Dlg.m_Addr,sizeof(m_szServerAdr));
    m_szPort=m_Dlg.m_Port;
    //建立计时器,每1秒尝试连接一次,直到连上或TryCount>10
    SetTimer(1,1000,NULL);
    TryCount=0;
    }
    }

    10. 添加Windows消息WM_TIMER响应函数OnTimer

    void CCSockClientDlg::OnTimer(UINT nIDEvent)
    {
    if (m_clientSocket.m_hSocket==INVALID_SOCKET)
    {
    BOOL bFlag=m_clientSocket.Create(0,SOCK_STREAM,FD_CONNECT);
    if(!bFlag)
    {
    AfxMessageBox("Socket Error!");
    m_clientSocket.Close();
    PostQuitMessage(0);
    return;
    }
    }
    m_clientSocket.Co
  • 2005-09-01

    在窗口中使用MCIWnd操作多媒体 - [VC专栏]

    (一)实验目的:
         在窗口中使用MCIWnd操作多媒体

    (二)实验内容:

      Windows中提供了一个MCIWnd窗口类,可以更加方便的在一个VC窗口中操作多媒体。
      使用MCIWnd,源文件中需要包含头文件 vfw.h,在Project->Settings->Link->Object/libray module中加入库 vfw32.lib。

    1、MCIWnd的创建

      MCIWnd子窗口的创建可使用MCIWndCreate函数:
        HWND MCIWndCreate(
          HWND hwndParent,   //父窗口句柄
          HINSTANCE hInstance, //应用程序的实例句柄
          DWORD dwStyle,    //显示风格
          LPSTR szFile     //多媒体文件名
        );
      返回的HWND可以保存下来,以供以后使用,也可不保存。
      该函数会在其父窗口上创建一个子窗口,类似于创建一个控间如按扭或列表框等。
      该子窗口会占据父窗口一定空间,可带有播放按钮、进度条、菜单按钮等。

    图3.3.1 MCIWnd子窗口
    示例:
     (1)建一个多文档的MFC应用程序。
     (2)在View类的头文件中加入变量:
        HWND m_mciWnd;
     (3)在View类中用ClassWizard重载OnInitialUpdate函数。
     (4)在此函数中加入代码:
         m_mciWnd=MCIWndCreate(m_hWnd, AfxGetInstanceHandle(), MCIWNDF_SHOWALL | MCIWNDF_RECORD, GetDocument()->GetPathName());
       这里,m_hWnd为此View窗口的HWND,
          AfxGetInstanceHandle()可取得本应用程序的实例句柄,
          MCIWNDF_flag们决定了子窗口中是否要加入播放按钮、录音按钮、菜单按钮、进度条等控件,
          GetDocument()->GetPathName()则可获得通过打开文件对话框取得的文件名。
     (5)编译运行。
     (6)在运行的程序中已经可以任意打开一个多媒体文件进行播放。例如 .wav、.avi、.mid文件。

    2、MCIWnd的使用

      如果在MCIWnd子窗口中有播放按钮、录音按钮、菜单按钮、进度条等控件,可以通过它们操作多媒体。
      如果象上例一样保存了MCIWndCreate函数返回的HWND,则不管子窗口中是否有控件,都可通过MCIWndxxxx函数操作多媒体。

     (7)在上面的程序中加入ID为 ID_NEW、ID_OPEN、ID_PLAY、ID_PLAYREVERSE、ID_RECORD、ID_SAVE、ID_STOP、ID_CLOSE 的菜单项或Toolbar按钮。
     (8)在 ID_NEW 的消息响应函数中加入:
         MCIWndNew(m_mciWnd,"waveaudio");
       MCIWnd子窗口可以建立一个新的音频多媒体文件。
     (9)在 ID_OPEN 的消息响应函数中加入:
         MCIWndOpen(m_mciWnd,"c://MyWav.wav",0);
       MCIWnd子窗口可以打开一个已存在的多媒体文件。
     (10)在 ID_PLAY 的消息响应函数中加入:
         MCIWndPlay(m_mciWnd);
       MCIWnd子窗口可以播放多媒体文件。
     (11)在 ID_PLAYREVERSE 的消息响应函数中加入:
         MCIWndPlayReverse(m_mciWnd);
       MCIWnd子窗口可以倒着播放视频多媒体文件。
     (12)在 ID_RECORD 的消息响应函数中加入:
         MCIWndRecord(m_mciWnd);
       MCIWnd子窗口可以录制音频多媒体文件。
     (13)在 ID_SAVE 的消息响应函数中加入:
         MCIWndSave(m_mciWnd,"c://MyWav.wav");
       MCIWnd子窗口可以保存已录制的音频多媒体文件。
     (14)在 ID_STOP 的消息响应函数中加入:
         MCIWndStop(m_mciWnd);
       MCIWnd子窗口可以停止正在播放或录制的多媒体文件。
     (15)在 ID_CLOSE 的消息响应函数中加入:
         MCIWndClose(m_mciWnd);
       MCIWnd子窗口可以关闭当前的多媒体文件,若要再使用,必须重新打开。
     (16)编译运行。
  • 2005-09-01

    使用DDE使应用程序可以添加新的程序组 - [VC专栏]

    使用DDE使应用程序可以添加新的程序组

    乌鲁木齐军医学院
    卢立建

    ---- 当应用程序安装完毕后,通常都会建立程序组,它们是怎么实现的呢?下面是一种使用DDE来添加新的程序组的例子。

    ---- 1.使用MFC新一项目PMGROUP(对话框形式),在对话框中添加三个Edit Box。

    ---- 2.使用Class Wizard,添加成员变量m_GroupName,m_ItemName,m_FileName,分别对应添加的三个Edit Box,它们将容纳输入的三个条目(组名,标记名,对应的文件名);

    ---- 3.在头文件STDAFX.H中,添加下列代码:

    #include < ddeml.h >

    ---- 4.在应用程序类CPGROUPApp的InitInstance函数中,添加下列代码:

    BOOL CPMGROUPApp::InitInstance()
    {
    //...
    //Initialze DDEML
    DdeInitialize(&dwDDEInst,NULL,APPCMD_CLIENTONLY,0);

    if (nResponse == IDOK)
    {
    // TODO: Place code here to handle when the dialog is
    // dismissed with OK
    if(!AddPMGroup(dlg.m_GroupName,dlg.m_ItemName,dlg.m_FileName))
    {
    ::MessageBox(NULL,"PMGroup:DDE Error","Error
    adding group and item.",MB_ICONHAND|MB_OK);
    }

    }
    //...
    }

    ---- 再给CPGROUPApp类添加一个成员变量dwDDEInst:

    ---- DWORD dwDDEInst;(用鼠标在ClassView的CPGROUPApp上点击右键,在弹出的菜单中选取择Add Member Variable,在对话中分别输入DWORD和dwDDEInst)

    ---- 因为它是一个以对话框为基础的应用程序,当单击确定后,就调用AddPMGroup来添加新的程序组。因此还要给CPGROUPApp类添加上AddPMGroup(用鼠标在ClassView的CPGROUPApp上点击右键,在弹出的菜单中选取择Add Member Function,在对话中分别输入int和AddPMGroup。将生成的int CPMGROUPApp::AddPMGroup()改为int CPMGROUPApp::AddPMGroup(CString &group,CString &item,CString &file),在CPGROUPApp中将int AddPMGroup()改为int AddPMGroup(CString &group,CString &item,CString &file)),AddPMGroup的内容为:

    int CPMGROOPApp::AddPMGroup(CString &group,CString &item,CString &file)
    {
    HSZ hszService=DdeCreateStringHandle(dwDDEInst,_T("PROGMAN"),CP_WINANSI);
    HSZ hszTopic=DdeCreateStringHandle(dwDDEInst,_T("PROGMAN"),CP_WINANSI);
    HCONV hConV=DdeConnect(dwDDEInst,hszTopic,hszService,NULL);
    DdeFreeStringHandle(dwDDEInst,hszService);
    DdeFreeStringHandle(dwDDEInst,hszTopic);
    if(!hConV)
    {
    return FALSE;
    }
    //主要内容
    CString cmd="[CreateGroup("+group+")]";//建立组的命令字
    DWORD dwResult;
    LPCTSTR data=(LPCTSTR)cmd;
    DdeClientTransaction((LPBYTE)data,cmd.GetLength (),
    hConV,NULL,CF_TEXT,XTYP_EXECUTE,
    1000,&dwResult);
    cmd="[AddItem("+file+","+item+")]";
    //建立"file"的图标,"item"为标记的命令字
    data=(LPCTSTR)cmd;
    DdeClientTransaction((LPBYTE)data,cmd.GetLength (),
    hConV,NULL,CF_TEXT,XTYP_EXECUTE,
    1000,&dwResult);
    return TRUE;
    }

    ---- 5.使用Class Wizard给CPMGROUPApp添加一个ExitInstance函数,输入下列代码:

    int CPMGROOPApp::ExitInstance()
    {
    // TODO: Add your specialized code here and/or call the base class
    DdeUninitialize(dwDDEInst);
    return CWinApp::ExitInstance();
    }

    ---- 6.按F5运行,分别输入组名、标名、文件名,按确定看看。

    ---- 7.当然我们也可用其它命令字对程序组进行其它操作:

    ---- [ShowGroup(My Group,1)]

    ---- 显示并激活名为My Group的组窗口

    ---- [DeleteGroup(My Group)]

    ---- 删除名为My Group的组

    ---- [ReloadGroup(My Group)]

    ---- 去除并重新装载名为My Group的组

    ---- [Additem(HELLO.EXE,HELLO)]

    ---- 为HELLO.EXE创建图标,标记为HELLO

    ---- [ReplaceItem(HELLO)]

    ---- 为名为HELLO的条目去除图标,并在这里插入下一条目

    ---- [DeleteItem(HELLO)]

    ---- 删除名为HELLO的条目

    ---- [ExitProgMan(1)]

    ---- 退出程序管理器,并存贮信息(0不存)




  • 2005-09-01

    实用技巧如何用VC++60编写查看二进制文件程序 - [VC专栏]

    实用技巧如何用VC++60编写查看二进制文件程序

    雷霆工作室 韩燕

    ---- 在计算机应用中,经常需要查看二进制文件的内容。目前,在各种VC++书籍中介绍
    查看文本文件的文章很多,但鲜有介绍查看二进制文件的文章。本文从功能设计、方案
    设计、编程实现以及技术要点等方面来简单介绍。
    ---- 1 功能设计
    ---- 显示界面见图1(略),将窗口客户区划分为三部分,左边列用于以16进制方式显示
    文件内容的相应位置,中间列用于以16进制方式显示文件内容,右边列用于显示文件内
    容对应的ASCII码的内容。为简化程序设计,没有打印功能。
    ---- 2 方案设计
    ---- 采用MFC的SDI(单文档界面)。由于在一屏内一般不可能显示整个文件的内容,所
    以选择视类的基类为CScrollView。二进制文件的读出与处理在文档类中完成,文件的显
    示与滚动由视类来实现。
    ---- 3 编程实现
    ---- 3.1 使用MFC AppWizard向导产生一应用框架在VC++的“File”菜单中,单击
    “New”,弹出一New对话框。在“Projects”页中选择“MFC AppWizard [exe]”,在
    “Project name”编辑框中填入“HexShow”(见图2(略)),按“OK”按钮,退出New对
    话框。在“MFC AppWizard Step 1”对话框中选择单选钮“Single document”,按
    “Next>”按钮,进入“MFC AppWizard Step 2 of 6”对话框,保持缺省选择,按
    “Next>”按钮,进入“MFC AppWizard Step 3 of 6”对话框,保持缺省选择,按
    “Next>”按钮,进入“MFC AppWizard Step 4 of 6”对话框,取消
    “Printing and print preview”选项(见图3(略)),按“Next>”按钮,进入
    “MFC AppWizard Step 5 of 6”对话框,保持缺省设置,继续按“Next>”按钮,进入
    “MFC AppWizard Step 6 of 6”对话框,在“Base class”组合框中选择CscrollView
    (见图4(略)), 按“Finish”按钮即可完成应用框架的定制。
    ---- 3.2 在文档类CHexShowDoc中增加文件的读出及处理工作---- 3.2.1 定义文档的成
    员变量,做好初始化及清理工作
    ---- 打开HexShowDoc.h文件,增加2个公共变量: CFile* m_pHexFile;
    LONG m_lFileLength;
    int m_nBytesPerLine;
    //每行显示多少个Byte然后,打开HexShowDoc.cpp文件,
    ---- 在类的构造函数中增加下列初始化代码: m_pHexFile = NULL;
    m_lFileLength = 0L;
    m_nBytesPerLine=16;
    //每行显示16个Byte在类的析构函数中增加下列清理代码:
    if (m_pHexFile != NULL)
    {
    m_pHexFile- >Close();
    delete m_pHexFile;
    m_pHexFile = NULL;
    }
    ---- 3.2.2 在OnOpenDocument()中打开文档---- 首先利用ClassWizard重载消息成员函
    数OnOpenDocument()。---- 在该成员函数的代码添加处增加下列代码:
    if (m_pHexFile != NULL)
    {
    m_pHexFile- >Close();
    delete m_pHexFile;
    }
    m_pHexFile = new CFile(lpszPathName,
    CFile::modeRead | CFile::typeBinary);
    if (!m_pHexFile)
    {
    AfxMessageBox("该文件打开错");
    return FALSE;
    }
    m_lFileLength = m_pHexFile- >GetLength();
    ---- 3.2.3 增加用于读文件及进行输出格式化处理的成员函数为CHexShowDoc类增加成
    员函数如下:
    BOOL CHexShowDoc::ReadFileAndProcess(CString &strLine, LONG lOffset)
    {
    LONG lPos;
    if (lOffset != -1L)
    lPos = m_pHexFile- >Seek(lOffset, CFile::begin);
    else
    lPos = m_pHexFile- >GetPosition();
    unsigned char szBuf[16];
    int nRet = m_pHexFile- >Read(szBuf, m_nBytesPerLine);
    if (nRet < = 0)
    return FALSE;
    CString sTemp;
    CString sChars;
    sTemp.Format(_T("%8.8lX : "), lPos);
    strLine = sTemp;
    for (int i = 0; i < nRet; i++)
    {
    if (i == 0)
    sTemp.Format(_T("%2.2X"), szBuf[i]);
    else if (i % 16 == 0)
    sTemp.Format(_T("=%2.2X"), szBuf[i]);
    else if (i % 8 == 0)
    sTemp.Format(_T(" - %2.2X"), szBuf[i]);
    else
    sTemp.Format(_T(" %2.2X"), szBuf[i]);
    if (_istprint(szBuf[i]))
    sChars += szBuf[i];
    else
    sChars += _T(’.’);
    strLine += sTemp;
    }
    if (nRet < m_nBytesPerLine)
    {
    CString sPad(_T(’ ’),
    2+3*(m_nBytesPerLine-nRet));
    strLine += sPad;
    }
    strLine += _T(" ");
    strLine += sChars;
    return TRUE;
    }
    ---- 3.3 在视中添加显示文件内容以及处理滚动操作
    ---- 3.3.1 变量定义以及初始化
    ---- a) 打开HexShowView.h文件,增加成员变量如下:
    CFont* m_pFont;//用于为显示文件内容选择字体
    ---- b) 在视的构造函数中为文件内容显示选择合适的字体
    //选择一种名为“Fixedsys”的字体,该字体使得字符的排列整齐
    LOGFONT m_logfont;
    memset(&m_logfont, 0, sizeof(m_logfont));
    _tcscpy(m_logfont.lfFaceName, _T("Fixedsys"));
    CClientDC dc(NULL);
    m_logfont.lfHeight = ::MulDiv
    (120, dc.GetDeviceCaps(LOGPIXELSY), 720);
    m_logfont.lfPitchAndFamily = FIXED_PITCH;
    m_pFont = new CFont;
    m_pFont- >CreateFontIndirect(&m_logfont);
    c) 将ChexShowView::OnInitialUp
  • 2005-09-01

    内存映射文件 - [VC专栏]

    内存映射文件

    假定程序需要阅读一个DIB文件, 一种方法是分配大小合适的一个缓冲区,打开文件,然后调用一个读函数将整个磁盘文件复制进缓冲区; 还有一种更有效的方法, 就是将一个地址范围直接映射到该文件中即可, 当进程访问一个内存页的时候,Windows将分配RAM并从磁盘中读取数据. 代码如下:
    HANDLE hFile = ::CreateFile(strPathname,GENERIC_READ,
    FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    ASSERT(hFile!=NULL);
    HANDLE hMap=
    ::CreateFileMapping(hFile,NULL,PAGE_READONLY,0,0,NULL);
    ASSERT(hMap != NULL);
    LPVOID lpvFile=::MapViewOfFile(hMap,FILE_MAP_READ,0,0,0);//map whole file
    DWORD dwFileSize = ::GetFileSize(hFile,NULL);
    // Use the file
    ::UnmapViewOfFile(lpvFile);
    ::CloseHandle(hMap);
    ::CloseHandle(hFile);
    lpvFile是起始地址,hMap变量包含有文件映射对象的句柄,该对象可以在进程之间共享.
  • 2005-09-01

    如何访问WebBrowser的滚动条 - [VC专栏]

    抱歉拖了很久才回复你的问题。
    WebBrowser的滚动条不是一般的Windows滚动条,用GetScrollPos或GetScrollInfo等API是不能访问的。下面的代码演示了在VC中如何通过HTML接口来访问浏览器的滚动条。

    HRESULT hr;
    IDispatch *pDisp = GetHtmlDocument();
    ASSERT( pDisp ); //if NULL, we failed

    // 获得Html文档指针
    IHTMLDocument2 *pDocument = NULL;
    hr = pDisp->QueryInterface( IID_IHTMLDocument2, (void**)&pDocument );
    ASSERT( SUCCEEDED( hr ) );
    ASSERT( pDocument );

    IHTMLElement *pBody = NULL;
    hr = pDocument->get_body( &pBody );
    ASSERT( SUCCEEDED( hr ) );
    ASSERT( pBody );

    // 从body获得IHTMLElement2接口指针,用以访问滚动条
    IHTMLElement2 *pElement = NULL;
    hr = pBody->QueryInterface(IID_IHTMLElement2,(void**)&pElement);
    ASSERT(SUCCEEDED(hr));
    ASSERT( pElement );

    // 向下滚动100个像素
    pElement->put_scrollTop( 100 );

    // 获得文档真正的高度,不是可见区域的高度
    long scroll_height;
    pElement->get_scrollHeight( &scroll_height );

    // 获得文档真正的宽度,不是可见区域的宽度
    long scroll_width;
    pElement->get_scrollWidth( &scroll_width );

    // 获得滚动条位置,从顶端开始
    long scroll_top;
    pElement->get_scrollTop( &scroll_top );
  • 2005-09-01

    放弃控制 - [VC专栏]

    如果在等待方式下,DispatchMessage必须等待处理完成后才能返回,在此之前将不能处理任何消息,
    而下面的代码可以做到即使没有消息到达程序的情况下也立即返回
    MSG message;
    While(::PeekMessage(&message,NULL,0,0,PM_REMOVE)){
    ::TranslateMessage(&message);
    ::DispatchMessage(&message);
    }
  • 2005-09-01

    访问资源 - [VC专栏]

    访问资源

    资源包括在EXE文件和DLL文件里面,因此它们所占用的虚拟地址空间在进程生存期间不发生改变.例如: 需要访问一个位图:
    LPVOID lpvResource=
    (LPVOID)::LoadResource(NULL,::FindResource(NULL,MAKEINTRESOURCE(IDB_REDBLOCKS),RT_BITMAP));
  • 2005-09-01

    多个定时器的使用 - [VC专栏]

    一、定时器的基本使用方法

    在编程时,会经常使用到定时器。使用定时器的方法比较简单,通常告诉WINDOWS一个时间间隔,然后WINDOWS以此时间间隔周期性触发程序。通常有两种方法来实现:发送WM_TIMER消息和调用应用程序定义的回调函数。

    1.1 用WM_TIMER来设置定时器

    先请看SetTimer这个API函数的原型

    UINT_PTR SetTimer(
    HWND hWnd, // 窗口句柄
    UINT_PTR nIDEvent, // 定时器ID,多个定时器时,可以通过该ID判断是哪个定时器
    UINT uElapse, // 时间间隔,单位为毫秒
    TIMERPROC lpTimerFunc // 回调函数
    );

    例如 SetTimer(m_hWnd,1,1000,NULL); //一个1秒触发一次的定时器
    在MFC程序中SetTimer被封装在CWnd类中,调用就不用指定窗口句柄了,例如:
    UINT SetTimer(1,100,NULL);
    函数反回值就是第一个参数值1,表示此定时器的ID号。

    第二个参数表示要等待100毫秒时间再重新处理一次。第三个参数在这种方法中一般用NULL。
    注意:设置第二个参数时要注意,如果设置的等待时间比处理时间短,程序就会出问题了。

    1.2 调用回调函数

    此方法首先写一个如下格式的回调函数
    void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime);
    然后再用SetTimer(1,100,TimerProc)函数来建一个定时器,第三个参数就是回调函数地址。

    二、多个定时器的实现与应用

    我们在安装定时器时都为其指定了ID,使用多个定时器时,该ID就发挥作用了。
    不使用MFC时,当接收到WM_TIMER消息,WPARAM wParam中的值便是该定时器的ID
    使用MFC时就更简单了,我们为其增加WM_TIME的消息处理函数OnTimer即可,请看如下例子 void CTimerTestDlg::OnTimer(UINT nIDEvent)
    {
    switch (nIDEvent)
    {
    case 24: ///处理ID为24的定时器
    Draw1();
    break;
    case 25: ///处理ID为25的定时器
    Draw2();
    break;
    }
    CDialog::OnTimer(nIDEvent);
    }
    当你用回调函数时,我们可以根据nTimerid的值来判断是哪个定时器,例如: void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime)
    {
    switch(nTimerid)
    {
    case 1: ///处理ID为1的定时器
    Do1();
    break;
    case 2: ///处理ID为2的定时器
    Do2();
    break;
    }
    }
    三、取消定时器

    不再使用定时器后,我们应该调用KillTimer来取消定时,KillTimer的原型如下
    BOOL KillTimer(
    HWND hWnd, // 窗口句柄
    UINT_PTR uIDEvent // ID
    );
    在MFC程序中我们可以直接调用KillTimer(int nIDEvent)来取消定时器。

    本文提供的例子代码在运行时就可以看到两个定时器都在工作,而且第一个在运行10次后就会被KILL掉。
    详细代码请看例子程序。
  • 2005-09-01

    对MFC封装 Windows 通讯 API的研究 - [VC专栏]

    对MFC封装 Windows 通讯 API的研究

    刘亚军,李丽,蒋洪睿

    桂 林 电 子 工 业 学 院97 研 541004

    一、 引 言
    近 年 来, 利 用Internet 进 行 网 际 间 通 讯, 在WWW 浏 览、FTP、Gopher
    这 些 常 规 服 务, 以 及 在 网 络 电 话、 多 媒 体 会 议 等 这 些 对 实 时
    性 要 求 严 格 的 应 用 中 成 为 研 究 的 热 点, 而 且 已 经 是 必 需 的 了。
    Windows 环 境 下 进 行 通 讯 程 序 设 计 的 最 基 本 方 法 是 应 用Windows
    Sockets 实 现 进 程 间 的 通 讯, 为 此 微 软 提 供 了 大 量 基 于Windows
    Sockets 的 通 讯API, 如WinSock API、WinInet API 和ISAPI, 并 一 直 致 力 于
    开 发 更 快、 更 容 易 的 通 讯API, 将 其 和MFC 集 成 在 一 起 以 使 通 讯
    编 程 越 来 越 容 易。
    MFC 是VC 编 程 环 境 最 重 要 的 组 成 部 分, 它 为 用 户 提 供 了 一
    大 批 预 先 定 义 的 类 和 成 员 函 数, 封 装 了 大 量 的Windows API。 同
    时VC 环 境 提 供 了 与MFC 对 象 和 代 码 一 起 工 作 的 专 用 工 具:
    AppStudio 源 程 序 编 辑 器、AppWizard 和Class Wizard。 应 用 MFC, 可 以
    使Windows 程 序 员 用 较 少 的 时 间 和 精 力 开 发 出 复 杂 的 通 讯 应 用
    程 序。
    本 文 根 据 笔 者 自 己 在 开 发 实 时 网 络 音 频 工 具FreeTalk 过 程 中
    的 一 些 经 验, 介 绍Windows 环 境 下 的 常 用API 和 封 装 它 们 的MFC 类,
    重 点 介 绍 使 用MFC 的CAsyncsocket 和CSocket 类 编 写 网 络 通 讯 程 序 的
    方 法, 这 两 个 类 封 装 了WinSock API, 并 使 他 们 更 容 易 使 用 和 更
    适 应 于MFC 编 程 环 境。
    二、Windows 环 境 下 的 通 讯API 和 相 应 的MFC 类
    1. Windows Sockets(WinSock)API

    ---- Windows Sockets 定 义 了Windows 的 网 络 编 程 接 口, 它 基 于 加 利 福 尼 亚 大 学 伯 克 利 分 校 的 伯 克 利Unix Sockets。Windows Sockets 既 包 括BSD 风 格 的 例 程, 还 加 入 了Windows 的 扩 展 部 分, 例 如 用 于 消 息 驱 动 的 扩 展 函 数。Windows Sockets 可 以 运 行 在 许 多 网 络 协 议 之 上, 包 括TCP/IP、XNS、DECNet、IPX/SPX 等。 在Win32 环 境 下,Windows Sockets 提 供 线 程 安 全。 通 过 微 软 与 标 准 组 织 的 努 力, 为WinSock 定 义 了 应 用 程 序 设 计 接 口(WinSock API), 可 以 非 常 方 便 地 利 用 下 层 的 网 络 协 议( 如TCP/IP) 进 行 网 络 通 讯。
    ---- 通 过 提 供 两 个 类CAsyncSocket 和CSocket,MFC 支 持 使 用WinSock API 通 讯 程 序 设 计。MFC 把 复 杂 的WinSock API 封 装 到 类 里, 这 使 得 编 写 应 用 程 序 更 容 易。CAsyncSocket 类 逐 个 封 装 了WinSock API, 为 高 级 网 络 程 序 员 提 供 了 更 加 有 力 而 灵 活 的 方 法。 这 个 类 基 于 程 序 员 了 解 网 络 通 讯 的 假 设, 目 的 是 为 了 在MFC 中 使 用WinSock, 程 序 员 有 责 任 处 理 诸 如 阻 塞、 字 节 顺 序 和 在Unicode 与MBCS 间 转 换 字 符 的 任 务。 为 了 给 程 序 员 提 供 更 方 便 的 接 口 以 自 动 处 理 这 些 任 务,MFC 给 出 了CSocket 类, 这 个 类 是 由CAsyncSocket 类 继 承 下 来 的, 它 提 供 了 比CAsyncSocket 更 高 层 的WinSock API 接 口。Csocket 类 和CsocketFile 类 与Carchive 类 一 起 合 作 来 管 理 发 送 和 接 收 的 数 据, 这 使 管 理 数 据 收 发 更 加 便 利。CSocket 对 象 提 供 阻 塞 模 式, 这 对 于Carchive 的 同 步 操 作 是 至 关 重 要 的。 阻 塞 函 数[ 比 如Receive()、Send()、ReceiveFrom()、SendTo() 和Accept()] 直 到 操 作 完 成 后 才 返 回 控 制 权, 因 此 如 果 需 要 低 层 控 制 和 高 效 率, 就 使 用CasyncSock 类; 如 果 需 要 方 便, 则 可 使 用Csocket 类。

    2.Win32 Internet(WinInet)API

    ---- 微 软 公 布 了 一 些 使Internet 应 用 程 序 的 设 计 比 以 前 更 快、 更 容 易 的API:WinInet API, 它 提 供 了 中 高 层 通 信 函 数, 这 使 访 问 主 要 的Internet 协 议 变 得 相 当 容 易。 这 些 函 数 在 程 序 员 和WinSock 驱 动 之 间 提 供 了 隔 离 层。 有4 类WinInet API 函 数: 通 用WinInet 函 数、WinInet 文 件 传 输 协 议(FTP) 函 数、WinInet Gopher 函 数、WinInet 超 文 本 传 输 协 议(HTTP) 函 数。

    ---- 事 实 上,MFC 把WinInet API 和ActiveX 技 术 封 装 进 类, 使Internet 编 程 更 加 容 易, 这 些 类 包 括CInternetSession、CInternetConnection、CInternetFile、CHttpConnection、CHttpFile、CGopherFile、CFtpConnection、CGopherConnection、CFileFind、CFtpFileFind、CGopherFileFind、CGopherLocator 和CInternetException。

    3.Internet 服 务 器API(ISAPI)

    ---- 微 软 的IIS 是 惟 一 与Windows NT Server 操 作 系 统 紧 密 集 成 的WWW 服 务
  • 2005-09-01

    窗口分割 - [VC专栏]

    该功能可将窗口分割成多个可滚动的面板,面板之间的边界称为分割条,可用分割条来调整每个面板的相对大小。要想增加窗口分割功能, 则必须修改主窗口类。首先,在主窗口类的头文件 MainFrm.h中添加以下代码:
    CsplitterWnd m-SWnd;
    Virtual BOOL OnCreateClient (LPCREATESTRUCTcs,CcreateContext *pContext);
    再在 MainFrm.cpp中添加成员函数 OnCreateClient的定义:
    BOOL CmainFrame::OnCreateCline(LPCREATESTRUCTcs,CcreateContext *p Context)
    {
    return m-SWnd.Creat(this,2,2,Csize(20,20),pContext);
    }
    新的CsplitterWnd类对象m-SWnd用于创建和管理分割窗口,该窗口中可以包含一个或多个面板。首次创建主窗口时,将调用成员函数 OnCreateClient。在缺省情况下,该函数创建一个填充主框窗口客户区的视图窗口。覆盖该函数后,将调用 CsplitterWnd的成员函数 Create来创建分割窗口。其中,第一个参数用于指定分割的父窗口(主窗口);第二个参数指定垂直方向上的面板个数为 2;第三个参数指定水平方向上的面板的个数;第四个参数用于设置每个面板的最小尺寸;第五个参数传递描述信息。上述分割窗口的每个面板都是由视图类对象管理的,当用户在某一面板内显示文档和图形时,必须在其它面板中重新绘制,从而在多个面板中均显示相同的内容。为此,必须调用显示文档类的 UpdateALLView成员函数来更新其它面板。此时,只需加入 pdoc->UpdataALLView(NULL) 即可。
  • 2005-09-01

    窗口的滚动 - [VC专栏]

    使用CscrollView代替Cview类即可实现滚动窗口。此时,系统生成OnInitialUpdate()成员函数:
    void CmyscrollView::OnInitialUpdat()
    {
    CscrollView::OnIntialUpdate();
    Csize sizeTotal;
    SizeTotal.cs=sizeToal.cy=100;
    SetScrollSizes(MM-TEXT,sizeTotal);
    }
    其中,cs和cy分别为滚动窗口的水平、垂直分量,表明窗口的水平、垂直方向尺寸小于 100像素单位时将出现水平方向滚动条和垂直方向滚动条。通过修改滚动尺寸,可改变出 现滚动条的最小窗口。例如,若“sizeTotal.cx=600;sizeTotal.cy=800; ”,则当窗口尺寸小于600*800时,就会出现滚动条。
  • 2005-09-01

    初始化应用程序屏幕的代码 - [VC专栏]

    BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
    {
    //应用程序窗口位于屏幕的*
    int xSize=::GetSystemMetrics(SM_CXSCREEN);
    int ySize=::GetSystemMetrics(SM_CYSCREEN);
    //应用程序窗口大小为整个屏幕大小的90%
    cs.cx=xSize*9/10;
    cs.cy=ySize*9/10;
    cs.x=(xSize-cs.cx)/2;
    cs.y=(ySize-cs.cy)/2;

    //从应用程序的标题栏中删除文档标题
    cs.style &=~FWS_ADDTOTITLE;

    //从应用程序的标题栏中去掉最小化和最大化按钮
    cs.style &=~(WS_MAXIMIZEBOX|WS_MINIMIZEBOX);

    //使应用程序窗口的大小固定,以致拖动窗口的右下角时没有反应
    cs.style &=~WS_THICKFRAME;

    return CMDIFrameWnd::PreCreateWindow(cs);

    }


    BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
    {
    cs.style=WS_CHILD|WS_VISIBLE|WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|FWS_ADDTOTITLE
    |WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_MAXIMIZE;
    return CMDIChildWnd::PreCreateWindow(cs);
    }
  • 2005-09-01

    VxD技术及其在实时反病毒中的应用 - [VC专栏]

    VxD技术及其在实时反病毒中的应用

    目前国内的Windows9x平台反病毒产品大多属静态反病毒软件,指导思想是"以杀为主",
    这一方式的缺点是病毒在被清除之前可能早已造成了严重危害一个好的反病毒软件应
    该是"以防为主,以杀为辅",在病毒入侵时就把它清除掉,这就是实时反病毒技术。

      ----Windows9x使用IntelCPU的Ring0和Ring3两个保护级。系统进程运行于Ring0,
    因而具有对系统全部资源的访问权和管理权;而普通用户进程运行于Ring3,只能访问
    自己的程序空间,不允许对系统资源进行直接访问许多操作受到限制。显然这种普通用
    户进程是无法胜任实时反病毒工作的,必须使后台监视进程运行在Ring0优先级,实现
    这一目的基础就是VxD技术。

      一、VxD技术的特点

      ----VxD即虚拟设备驱动程序,用作Windows9x系统和物理设备之间的接口。但它不
    仅适用于硬件设备,也适用于按VxD规范所编制的各种软件"设备"

      ----VxD技术的实质是:通过加载具有Ring0最高优先级的VxD,运行于Ring3上的应
    用程序能够以一定的接口控制VxD的动作,从而达到控制系统的目的。实时反病毒软件之
    所以要使用VxD技术,关键有二:(1)VxD拥有系统最高运
    行权限(2)许多Windows9x系统底层功能只能在VxD中调用,应用程序如果要用必须编个
    VxD作为中介。VxD作为应用程序在系统中的一个代理,应用程序通过它来完成任何自
    己本身做不到的事情,通过这一手段,Windows9x系统为普通应用程序留下了扩充接口。
    很不幸,这一技术同样为病毒所利用,CIH病毒正是利用了VxD技术才得以驻留内存、
    传染执行文件、毁坏硬盘和FlashBIOS。

     ----Windows9x系统下有众多的VxD,每个VxD可提供4种服务,即PM(保护模式)API、
    V86(虚拟86)API、Win32服务和VxD服务,前3种分别供应用程序在16位保护模式、V86
    模式以及32位保护模式下调用,VxD服务则只供其他VxD使用用户开发的VxD可提供任意
    上述服务。除此之外,应用程序还可通过调用API函数DeviceIoControl与支持IOCTL接
    口的VxD进行通信,执行Win32API不支持的系统低级操作。

     二、VxD技术的实现

     ----VxD的操作基于寄存器,所以一般用汇编语言编写,它的关键部分是一个和普通
    窗口的消息处理过程WndProc相类似的控制过程,不同之处在于它的处理对象是系统发
    来的控制消息。这些消息共51种,在VxD自加载至卸出整个生命周期内,操作系统不断
    向它发送各种控制消息,VxD根据自己的需要选择处理,其余的忽略。系统向VxD发送控
    制消息时将消息代号放在EAX寄存器中并在EBX寄存器中放系统虚拟机(VM)句柄。

     ----对动态VxD来说,最重要的消息有三个:SYS_DYNAMIC_DEVICE_INIT、
    SYS_DYNAMIC_DEVICE_EXIT以及W32_DEVICEIOCONTROL,消息代号分别是1Bh、1Ch、23h。
    当VxD被动态加载至内存时。
      系统向其发送SYS_DYNAMIC_DEVICE_INIT消息,VxD应在此时完成初始化设置并建立
    必要的数据结构;当VxD将被卸出内存时,系统向其发送SYS_DYNAMIC_DEVICE_EXIT消息
    VxD在收到后应清除所作设置并释放相关数据结构;当应用程序调用API函数
    DeviceIoControl与VxD进行通信时,系统向VxD发送W32_DEVICEIOCONTROL消息,它是应
    用程序和VxD联系的重要手段,此时ESI寄存器指向一个DIOCParams结构,VxD从输入缓冲
    区获取应用程序传来数据,相应处理后将结果放在输出缓冲区回送应用程序,达到相互
    传递数据的目的。

     ----应用程序向VxD发出DeviceIoControl调用时,第2个参数用于指定进行何种控制,
    控制过程从DIOCParams结构+0Ch处取得此控制码再进行相应处理控制码的代号和含义
    由应用程序和VxD自行约定,系统预定义了DIOC_GETVERSION(0)和
    DIOC_CLOSEHANDLE(-1)两个控制码,当应用程序调用API函数
    CreateFile(".VxDName",...)动态加载一VxD时,系统首先向该VxD的控制过程发送
    SYS_DYNAMIC_DEVICE_INIT控制消息,若VxD返回成功,系统将再次向VxD发送带有控制
    码DIOC_OPEN(即DIOC_GETVERSION,值为0)的W32_DEVICEIOCONTROL消息以决定此VxD是
    否能够支持设备IOCTL接口,VxD必须清零EAX寄存器以表明支持IOCTL接口,这时
    CreateFile将返回一个设备句柄hDevice,通过它应用程序才能使用DeviceIoControl
    函数对VxD进行控制。同一个VxD可用CreateFile打开多次,每次打开时都会返回此VxD
    的一个唯一句柄,但是系统内存中只保留一份VxD,系统为每个VxD维护一个引用计数,
    每打开一次计数值加1。当应用程序调用API函数CloseHandle(hDevice)关闭VxD句柄时,
    VxD将收到系统发来的带控制码DIOC_CLOSEHANDLEW32_DEVICEIOCONTROL消息,同时该
    VxD的引用计数减1,当最终引用计数为0时,系统向VxD发送控制消息
    SYS_DYNAMIC_DEVICE_EXIT,然后将其从内存中清除。在极少数情况下应用程序也可调
    用API函数DeleteFile(".VxDName")忽略引用计数的值直接将VxD卸出内存,这将给使用
    同一VxD的其他应用程序造成毁灭性影响,应避免使用。

     --一个典型的VxD控制过程代码如下:

     BeginProcVXD_Control
     cmpeax,1Bh
     ;SYS_DYNAMIC_DEVICE_IN
  • 2005-09-01

    WEB服务器上基于MAC地址身份验证的实现 - [VC专栏]

    WEB服务器上基于MAC地址身份验证的实现

    厦门电业局
    李吉林、陈金旺

    ---- 随着Internet技术的飞速发展,企业信息系统逐步转向Intranet化,建立WEB服务器作为企业信息的发布和交换平台。但是,从企业信息安全角度出发,应该建立企业信息有限度的开放机制,保证用户在规定的权限范围内获取相应的信息。常规的安全检查方法就是设立用户身份验证机制,即在WEB服务器上进行口令 和IP地址过滤。可是,企业内部网络工作站的IP地址是动态分配,没有固定的地址,而且容易被假冒。口令检查方式又增加用户记忆上的负担,容易被别人获取。由于网络工作站上的网卡地址是全世界唯一和固定不变的,可以作为安全检查的身份标识。当用户通过浏览器访问企业的WEB服务器时候,采用的网络通讯HTTP协议是工作在TCP/IP协议上,是基于网络层的协议,而MAC地址是在链路层上的设备才会关心的东西,所以通常情况下两台机器互访是通过广播或ARP(Address Resulution Protocol)协议得到相互的MAC地址后进行通讯,而基于TCP/IP协议编程是很难获取对方的MAC地址。在因为路由器要准确的投递网络数据包,拥有网络上最完整ARP地址识别表,该表存放于路由器上的SNMP MIB管理信息库中,所以应从这些网络设备入手获取MAC地址。根据该思路,我们采用了根据用户网络MAC地址进行身份验证的安全机制,已在我局的计算机城域网上实际运行一年多时间,证明是一种简易有效的安全检查手段。下面就实现该机制的技术要点分别展开介绍如下:
    ---- 一、技术思路
    ---- 浏览器/服务器的模式是一种把标准输入输出从定向到客户端的浏览器上,而运算过程在服务器上的一种操作,所以当某个浏览器访问相应的主页时,它的一些基本信息就会提交给了服务器,这些信息包括了浏览器的名称、版本号等,同时也包括了我们所关心的客户端的IP地址,该地址存放于环境变量REMOTE_ADDR中。
    ---- 当我们获得这个地址后,下一步工作就是去询问该机器的MAC地址,这也是编程最重要的一步。要从路由器中取到ARP的信息,就要进行基于网络的SNMP(简单网络管理协议)编程。SNMP协议目前最常用的有两个版本,通常称之为v1和v2。一般情况下v2向下兼容v1版但有时也有例外。SNMP MIB管理信息库的存储方式是一种目录树的结构,而且它总是开始于:iso.org.dod翻译成数字就是.1.3.6,这就是我们所说的MIB(Management Information Base,管理信息库)的格式,而关于物理地址的MIB就是:
    iso.org.dod.internet.mgmt.mib.at.atTable.atEntry.
    atPhysAddress(1.3.6.1.2.1.3.1.1.2)。
    ---- 所以基于上述思路, 该程序整个处理流程就是这样设计的,当有人访问WEB服务器的主页,便激活MAC.EXE(用C语言编写),而当该程序获得客户机的IP地址后,自动激活一个叫sn..class的java程序,sn.class便通过相应的MIB值向路由器询问此IP的MAC地址,并将结果传递给MAC.EXE,而MAC.EXE将获取的这个MAC地址到自身WEB服务器上的已登记合法MAC地址库内查找, 若已在MAC地址库登记的,则通过身份验证,把其相应的主页信息呈现在客户端的屏幕上;否则,验证失败,拒绝访问。
    ---- 二、SNMP协议的MIB管理信息库
    ---- 由于上述技术思路涉及SNMP协议及其MIB管理信息库,这里做一下简要说明。SNMP标准主要由三部分组成:简单网络管理协议(SNMP);管理信息结构(Structure ofanagement Information,简称SMI,RFC1155标准)和管理信息库(MIB ,RFC1156、RFC1158标准)。
    ---- 管理信息结构(SMI)和管理信息库(MIB)两个协议是关于管理信息的标准,它们规定了被管理的网络对象的定义格式,MIB库中都包含哪些对象,以及怎样访问这些对象等等。
    ---- SMI协议规定了定义和标识MIB变量的一组原则。它规定所有的MIB变量必须用ASN.1(即抽象语法表示法I,它是一种描述数据结构的通用方法,作为OSI研究的一部分,由ISO推出)来定义。

    图1 MIB的树型示意图

    ---- 每个MIB变量都有一个名称用来标识。
    在SMI中,这个名称以对象标识符(Object Identifier)来表示。对象标识符相互关联,共同构成一个分层结构。在这个分层结构里,一个对象的标识符是由从根出发到对象所在节点的途中所经过的一个数字标号序列组成。如图1中,Internet的对象标识符就是1.3.6.1。对象标识符的命名有专门的机构负责。在Internet节点下的mgmt节点,专门为管理信息库分配了一个子树,名为mib(1)。所有的MIB变量都在mib节点下,因此它们的名
    称(对象标识符)都以iso.org.dod.internet.mgmt.mib开头,数字表示是1.3.6.1.2.1。
    ---- 路由器中的路由表 MIB中就有网络中工作站IP地 址与 MAC地址的对应表 在CISCO路由器的路由表中 ,这种信息一般放在树型结构的{1.3.6.1.2.1.3.1.1.2.0.0.1.47)位置中,比如:一个 IP地址为10.142.168.1的机器它 的 MAC地址就存放在MIB表中{1.3.6.1.2.1.3.1.1.2.0.0.1.47}+{10.142.168.1}的这个位 置上知道了这个表结构后我们就可以通过编写 CGI程序来调用 SNMP代理读取 MIB管理信息表中的 MAC地址。
    ---- 三、获取MAC地址的JAVA接口程序
    ---- 我们可以通过好几种方法来调用 SNMP代理,如用 C++来调用 WINT 下 SNMP自带的 SNMP AP
  • 2005-09-01

    动画窗口的实现-VC++实例一则 - [VC专栏]

    摘要:
    本文通过对AnimateWindow函数的分析,介绍动画窗口的实现原理,同时指出了在运用AnimateWindow函数时在编译中会遇到的一些问题以及处理方法。

    关键词:动画窗口,AnimateWindow,MSDN

    一、引言

    俗话说"佛靠金装,人靠衣装",一个好的软件如果能配上精美的界面一定会让更多的用户认同它。喜欢上网的朋友对NetAnt(网络蚂蚁)这个软件一定不会陌生,它的下载速度,断点续传的功能都给我们留下了深刻的印象,同时它的软件界面也是相当棒的。在NetAnt的1.23版中,当下载任务完成或出错时,在主窗口的*会以动画的方式展开一个窗口,报告当前下载的状况;当用户关闭窗口时,窗口又以收缩的方式关闭起来。那么这个动画窗口是怎样实现的呢?下面我们就来讨论一下在VC中如何实现这种动画窗口。

    二、编程原理

    要实现这种动画窗口的编程效果,主要用到Windows API中的AnimateWindow函数,通过在窗口的创建或消毁过程中运用该函数,来实现开启和关闭程序时达到所希望的动画窗口效果。AnimateWindow函数所提供的动画效果十分丰富,我们可以在自己的程序中选择各种不同的动画效果,增强程序的趣味性。为使读者对AnimateWindow函数有一个基本了解,我们先对该函数做一个简单介绍:

    函数原型:BOOL AnimateWindow(HWND hWnd,DWORD dwTime,DWORD dwFlags)。

    函数功能:该函数能在显示与隐藏窗口时产生两种特殊类型的动画效果:滚动动画和滑动动画。

    参数含义:
    hWnd:指定产生动画的窗口的句柄。
    dwTime:指明动画持续的时间(以微秒计),完成一个动画的标准时间为200微秒。
    dwFags:指定动画类型。这个参数可以是一个或多个下列标志的组合。标志描述:
    AW_SLIDE:使用滑动类型。缺省则为滚动动画类型。当使用AW_CENTER标志时,这个标志就被忽略。
    AW_ACTIVATE:激活窗口。在使用了AW_HIDE标志后不能使用这个标志。
    AW_BLEND:实现淡出效果。只有当hWnd为顶层窗口的时候才可以使用此标志。
    AW_HIDE:隐藏窗口,缺省则显示窗口。
    AW_CENTER:若使用了AW_HIDE标志,则使窗口向内重叠,即收缩窗口;若未使用AW_HIDE标志,则使窗口向外扩展,即展开窗口。
    AW_HOR_POSITIVE:自左向右显示窗口。该标志可以在滚动动画和滑动动画中使用。当使用AW_CENTER标志时,该标志将被忽略。
    AW_VER_POSITIVE:自顶向下显示窗口。该标志可以在滚动动画和滑动动画中使用。当使用AW_CENTER标志时,该标志将被忽略。
    AW_VER_NEGATIVE:自下向上显示窗口。该标志可以在滚动动画和滑动动画中使用。当使用AW_CENTER标志时,该标志将被忽略。


    返回值:如果函数成功,返回值为非零;如果函数失败,返回值为零。在下列情况下函数将失败:

    窗口使用了窗口边界;窗口已经可见仍要显示窗口;窗口已经隐藏仍要隐藏窗口。

    三、动画窗口的实现

    下面就以一个简单的单文档程序为例,说明如何在VC中使用AnimateWindow函数来实现打开和关闭程序时的动画效果。基于多文档与对话框的程序所用方法类似,本文就不一一介绍。笔者所使用的开发环境为:WindowsME,Visual C++6。

    1、建立一个MFC AppWizard(exe)应用工程Animate。

    在MFC AppWizard向导的第一步中选择Single document,再点击按键Finish->OK完成工程建立。

    2、在CMainFrame::OnCreate函数中增加黑体加粗部分语句。
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    ……
    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_wndToolBar);
    AnimateWindow(GetSafeHwnd(),1000,AW_CENTER);
    return 0;
    }


    3、使用MFC ClassWizard增加消息处理函数

    使用ClassWizard在CMainFrame类中增加WM_CLOSE消息处理函数,并增加以下语句。
    void CMainFrame::OnClose()
    {
    // TODO: Add your message handler code here and/or call default
    AnimateWindow(GetSafeHwnd(),1000,AW_HIDE|AW_CENTER);
    CFrameWnd::OnClose();
    }

    四、编译时出现的问题

    在实现动画窗口的程序时,笔者发现如果直接在VC中使用AnimateWindow函数,在编译时会报告出错。以上述程序为例,在编译时系统会报告:
    ’AnimateWindow’ : undeclared identifier

    ’AW_HIDE’ : undeclared identifier

    ’AW_CENTER’ : undeclared identifier

    通过错误提示可以看出是编译系统认为AnimateWindow函数和AW_HIDE、AW_CENTER两个参数没有定义。因该函数是定义在Winuser.h头文件中的,于时,笔者显示地在程序中定义了对该头文件的包含,编译时却仍然出现相同的错误。为什么在VC中编译不能通过呢?通过查阅MSDN笔者发现在MSDN中明确提到WindowsNT5.0和Windows98以上版本均支持该函数。通过笔者的研究发现,问题出在定义AnimateWindow函数的头文件Winuser.h中,在VC安装目录下进入include子目录,用EDIT打开Winuser.h文件,按F3键查找AnimateWindow,可以发现有两处定义,一处是定义该函数中使