VC++中使用使用winnet类获取网页内容

 

  • 微软提供的Winnet类是一个应用层的网络通信组件,

    它可以使你的应用程序很容易的实现http、ftp、gopher等协议而不需要

    你去深入的了解协议本身的规范。而之前,如果要想做类似的应用,我们

    必须了解socket编程并且要对协议本身非常熟悉,哪怕是一个非常非常

    简单的程序。



    下面是codeguru上的一个使用wininet类的例子,它能够从给定的url地址中

    获取该文件。



    这个例子实现了两个方法:

    cstring getwebpage(const cstring& url);

    void seterrormessage(cstring s);


    getwebpage是主要的方法,后面跟的url地址必须是一个完整的url地址,

    比如http://blog.csdn.net/cqq。

    seterrormessage方法, 能够处理程序中发生的错误。




    下面来看代码:

    /*
    //------------------------------------------------------------------------------------------------------------------
    // webworld.h: interface for the cwebworld class.
    //一个封装的类cwebworld,webworld.h是该类的头文件
    //------------------------------------------------------------------------------------------------------------------
    */

    #include "wininet.h" //包含wininet.h ,在mfc中也可以是afxinet.h

    class cwebworld
    {
    public:
    void seterrormessage(cstring s); //声明seterrormessage方法
    cstring getwebpage(const cstring& url); //声明getwebpage方法
    cwebworld(); //构造函数
    virtual ~cwebworld();

    private:
    cstring m_errormessage;
    hinternet m_session; //声明一个http连接的句柄
    };



    /*
    //------------------------------------------------------------------------------------------------------------------
    // webworld.cpp: implementation of the cwebworld class.
    // cwebworld类的具体实现
    //------------------------------------------------------------------------------------------------------------------
    */

    #include "stdafx.h"
    #include "webthief.h"


    #ifdef _debug
    #undef this_file
    static char this_file[]=__file__;
    #define new debug_new
    #endif

    #define agent_name "codegurubrowser1.0"

    //
    // construction/destruction
    //

    cwebworld::cwebworld()
    {
    dword dwerror;

    // initialize the win32 internet functions
    // 构造函数中初始化网络连接
    m_session = ::internetopen(agent_name,
    internet_open_type_preconfig, // use registry settings.
    null, // proxy name. null indicates use default.
    null, // list of local servers. null indicates default.
    0) ;

    dwerror = getlasterror();
    }

    cwebworld::~cwebworld()
    {
    // closing the session
    //虚构函数中释放连接
    ::internetclosehandle(m_session);
    }

    cstring cwebworld::getwebpage(const cstring& url)
    {
    hinternet hhttpfile;
    char szsizebuffer[32];
    dword dwlengthsizebuffer = sizeof(szsizebuffer);
    dword dwfilesize;
    dword dwbytesread;
    bool bsuccessful;
    cstring contents;

    // setting default error message
    contents = m_errormessage;

    // opening the url and getting a handle for http file
    hhttpfile = internetopenurl(m_session, (const char *) url, null, 0, 0, 0);

    if (hhttpfile)
    {
    // getting the size of http files
    bool bquery = ::httpqueryinfo(hhttpfile,http_query_content_length, szsizebuffer, &dwlengthsizebuffer, null) ;

    if(bquery==true)
    {
    // allocating the memory space for http file contents
    dwfilesize=atol(szsizebuffer);
    lpstr szcontents = contents.getbuffer(dwfilesize);

    // read the http file
    bool bread = ::internetreadfile(hhttpfile, szcontents, dwfilesize, &dwbytesread);

    if (bread)
    bsuccessful = true;

    ::internetclosehandle(hhttpfile); // close the connection.
    }

    }
    else
    {
    // connection failed.
    bsuccessful = false;
    }
    return contents;
    }

    void cwebworld::seterrormessage(cstring s)
    {
    m_errormessage = s;
    }


    下面是关于上面的类的使用方法:

    cwebworld a;
    cstring pagecontent;

  • 作者:经乾


    一、Windows的控制面板应用程序
    ---- 在Windows的系统目录下可以找到控制面板应用程序,它们是一些扩展名
    为cpl的dll,通常用来提供配置服务,如配置网络硬件和软件用的ncpa.cpl,配
    置桌面用的desk.cpl等,它们的父窗口都是桌面。运行这类程序的方法很多:双
    击控制面板中的图标,双击系统目录下的cpl文件,或者使用如下DOS命令:
    rundll32 shell32.dll,Control_RunDLL
    < cpl文件> [@n]或者
    control < cpl文件> [@n]

    ---- 其中@n用于指定运行运行哪一个小程序(一个cpl文件中可包含多个小程
    序),若不带此参数则相当于@0,即运行第一个小程序。例如:
    ---- Rundll32 shell32.dll,Control_RunDLL main.cpl @n

    ---- 若不带@n参数,则运行鼠标设置;@1则运行键盘设置;@2则运行打印机设
    置;@3则显示系统字体。

    ---- Control_RunDLL是shell32.dll中定义的一个函数,大小写敏感,用于启动
    控制面板。

    ---- 通过控制面板应用程序来提供配置服务是一种很好的方法,那么如何设计
    这种应用程序呢?下面以Visual C++6.0为例进行介绍。

    二、开发控制面板应用程序
    ---- 若希望一个文件传输程序启动后自动连接到某一个站点,就需要为其提供
    一些缺省值,如:服务器名、用户名、口令等。下面就开发一个控制面板应用
    程序来提供这些缺省值,具体步骤为:
    ---- 1.创建一个"MFC AppWizard (dll)"类型的项目,命名为:MyCplApp,选
    择"Regular DLL With MFC statically linked",使它的运行不依赖于其它任何DLL。

    ---- 2.选择Project菜单下的Settings项或按Alt+F7,在Link页中将输出文件
    名改为:

    ---- < WindowsDir >/< SystemDir >/Mycplapp.cpl,在Debug页中将
    "Executable for debug session"改为:< WindowsDir >/< SystemDir >/Control.exe,
    以便直接用控制面板运行。对于Win9x,< SystemDir >为System,对于WinNT,
    < SystemDir >为System32。

    ---- 3.从MSDN Library Visual Studio 6.0光盘的Smples目录中找到
    Ctrlpan.cpp 和Ctrlpan.h 文件,并把它们加到项目中。

    ---- 4.创建如图所示对话框,代号为:IDD_MYDIALOG,用Class Wizard创建
    一个CMyDialog类。为每个控件创建成员变量,如"FTP服务器"对应的成员变
    量定义为:CString m_strServer。



    ---- 5.引入或新建一个ICON,代号为:IDI_MYICON,用于控制面板中显示。

    ---- 6.从CControlPanel类(在Ctrlpan.h中定义)继承一个新类CMyPanel,修改
    MyPanel.h文件:

    #include "Ctrlpan.h"
    class CMyPanel : public CControlPanel
    { public:
    virtual LONG OnInquire(UINT uAppNum, NEWCPLINFO* pInfo);
    virtual LONG OnDblclk(HWND hwndCPl,
    UINT uAppNum, LONG lData);
    };//两个虚拟函数由读者加入

    ---- 7.在MyPanel.cpp中实现MyPanel.h中定义的两个虚拟函数:
    LONG CMyPanel::OnInquire(UINT uAppNum,
    NEWCPLINFO* pInfo)
    { //此函数在控制面板打开时被调用,
    用于获取资源信息,即填充pInfo结构
    pInfo- >dwSize = sizeof(NEWCPLINFO);//指定结构长度
    pInfo- >dwFlags = 0;//此成员忽略
    pInfo- >dwHelpContext = 0; //此成员忽略
    pInfo- >lData = 0;//小程序传递给应用程序的LONG类型的值
    pInfo- >hIcon= ::LoadIcon(AfxGetResourceHandle(),
    MAKEINTRESOURCE(IDI_MYICON));//加载图标
    strcpy(pInfo- >szName, "FTP设置");//设置名称
    strcpy(pInfo- >szInfo, "设置FTP缺省信息");//设置描述
    strcpy(pInfo- >szHelpFile, "");//此成员忽略
    return 0; //不发送CPL_INQUIRE消息
    }
    LONG CMyPanel::OnDblclk(HWND hwndCPl,
    UINT uAppNum, LONG lData)
    { //双击控制面板中的图标时,此函数被调用,
    用于读取或保存设置信息。
    CMyDialog dlg(CWnd::FromHandle(hwndCPl));
    //用父窗口句柄初始化对话框
    HKEY hcpl;//主键,用于读写注册表
    if (RegOpenKeyEx(HKEY_CURRENT_USER,
    "Control Panel//FTPSet",0,
    KEY_QUERY_VALUE, &hcpl) == ERROR_SUCCESS)
    { DWORD dwType = 1;//字符串类型REG_SZ
    DWORD dwSize;//字符串长度
    RegQueryValueEx(hcpl,"FTPServer",NULL,&dwType,
    (BYTE*)(LPCTSTR)dlg.m_strServer,&dwSize);
    RegCloseKey(hcpl);
    }//查询注册表,读取以前的设置信息,上面仅以m_strServer为例。
    RegCloseKey(hcpl);
    if(dlg.DoModal()!=IDOK) return 0;//执行对话框,
    若用户点击"取消"则返回
    DWORD dwDisp;//用于接收创建主键的返回值
    if (RegCreateKeyEx(HKEY_CURRENT_USER,
    "Control Panel//FTPSet",0,"",
    REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
    &hcpl, &dwDisp) == ERROR_SUCCESS)
    { RegSetValueEx(hcpl,"FTPServer",0,REG_SZ,
    (BYTE*)(LPCTSTR)dlg.m_strServer,
    dlg.m_strServer.GetLength());
    RegCloseKey(hcpl);//将对话框中设置保存到注册表
    }return 0;
    }

    ---- 8.修
  • VC5开发工具提供了现成的窗口、控制与工具条的制作手段,大大简化了界面的开发
    过程,并且使得开发出的界面具有组态软件风格,使用起来方便、灵活、简单易学。

    本文以一实例介绍如何实现三个独立的分离窗口:监视窗口,控制窗口和动画窗口,
    并以图1中的进水和温度值传递为例,介绍如何实现控制功能和不同窗口间的数据共享,
    并介绍实现无闪烁动画的方法。
    (图略)

    如图1,将工作台分离成为三个窗口,动画窗口用于模拟锅炉的进、出水、升温的画
    面显示,其中的画面与系统采集的数据相对应。控制窗口用于实现预设温度值,调节水位、
    控制加热、暂停等功能。监视窗口用来实时跟踪采样的温度值,作出温度--时间曲线。

    一、创立分离窗口

    要实现多窗口显示,必须使用CSplitterWnd类,将窗口分成三个子窗口,然后将各个
    功能类与窗口联系起来。在创建应用程序时,在第一步中选择Single Document Interface, 并
    选用中文字库,在第4步中按下Advanced,选择Use Split Window选项。设定应用程序名
    为Animation。目前我们只有一个视类CAnimationView,它将与动画窗口对应,此外我们
    还要生成具有对话功能的监视窗口(对应CShowView类)和控制窗口(对应 CControlView
    类)。在Resource View中调出上下文菜单并选择Insert,选择属性为IDD_FORMVIEW,创
    建监视对话框IDD_SHOWVIEW和控制对话框IDD_CONTROLVIEW,并单击鼠标右键在
    Properties选择项中选择中文字库。然后编辑IDD_CONTROLVIEW:利用VC5提供的控件
    生成器生成ID名为IDC_SETTEMPERATURE的文字编辑域,并生成Caption为“设置温
    度初始值”。再利用button生成器,生成控件IDC_WATERIN,IDC_CONFIRM,Caption
    分别为“进水”和“确认”。再利用ClassWizard,创建基于CFormView类的新类,分别为
    CShowView和CControlView类,并将它们与刚创建的两个对话框联系。

    在CMainFrame类中,重载OnCreateClient()函数创建三个静态分离窗口,先在
    MainFrame.h中声明所需变量:

    protected:
    CSplitterWnd m_wndSplitter2;
    转入MainFrame.cpp程序,在开头处包含头文件“ShowView.h”,向下找到函数
    OnCreateClient(),添加如下代码,生成两个窗口,三个视图:
    BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT/*lpcs*/,CCreateContext* pContext)
    {
    //先分裂成两个窗口,一行两列
    if(!m_wndSplitter.CreateStatic(this,1,2))
    {
    TRACE0("Failed to CreateStaticSplitter/n");
    return FALSE;
    }

    // 加上动画窗口,将其放在左边
    if(!m_wndSplitter.CreateView(0,0,pContext->m_pNewViewClass,CSize(425,50),pContext))
    {
    TRACE0("Failed to create first pane/n");
    return FALSE;
    }

    //将第二个窗口再一分为二
    if(!m_wndSplitter2.CreateStatic(
    &m_wndSplitter, //原来的m_wndSplitter是父指针,m_wndSplitter2是子指针
    2,1, //窗口分为两行,一列
    WS_CHILD|WS_VISIBLE|WS_BORDER,m_wndSplitter.IdFromRowCol(0,1)))
    {
    TRACE0("Failed to create nested splitter/n");
    return FALSE;
    }

    //增加两个视图,并调整视图大小
    if(!m_wndSplitter2.CreateView(0,0,
    RUNTIME_CLASS(CShowView),CSize(0,175),pContext))
    {
    TRACE0("Failed to create second pane/n");
    return FALSE;
    }

    if(!m_wndSplitter2.CreateView(1,0,RUNTIME_CLASS(CControlView),
    CSize(0,0),pContext))
    {
    TRACE0("Failed to create third pane/n");
    return FALSE;
    }
    return TRUE;
    }

    再转入Animation.cpp中,修改InitInstance()函数,将其中的m_pMainWnd->ShowWindow
    (SW_SHOW),改为m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);至此,我们
    可以生成图1的界面框架。

    二、动画显示窗口的实现

    动画是通过调用一幅幅的图片来实现的,因此先将所需的画面载入资源BITMAP中,
    并按顺序编辑它们的ID号,然后在定时器中,每隔一定的时间调用一次动画函数。第一
    步先生成定时器,用ClassWizard给CAnimationView添加消息处理程序:OnCreate()函数
    对应于消息WM_CREATE,OnTimer()函数对应于消息WM_TIMER。编辑函数OnCreate(),
    生成每隔0.1秒的时钟。

    int CAnimationView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    if(CView::OnCreate(lpCreateStruct)= = -1)
    return -1;
    SetTimer(2,100,NULL); //产生每隔0.1秒的时钟
    return 0;
    }
    在函数OnTimer()中,调用动画服务函数ServicedAnimation(),该函数根据系统情况作出
    无闪烁动画,并可以根据不同的功能,选择画面。
    void CAnimationView::OnTimer(UINT nIDEvent)
    {
    CClientDC ClientDC(this);
    ServicedAnimation(&ClientDC); //调用动画服务函数
    CView::OnTimer(nIDEvent);
    }
    ServicedAnimation()用于检查系统的时钟,并能计算从发生最后一个动画事件开始计算
    起所经过的时间,然后这个函数检查本帧动画的延迟时间,并决定是否到达了另一次更新
    的时间,
  • ---- 读 了 贵 报9 月7 日《 学 习 与 编 程》 版 刊 登 的 宋 立 波 先
    生 的 文 章, 本 文 介 绍 另 一 种 方 法 来 制 作 封 面, 此 方 法 对
    于 单 文 档、 多 文 档 及 对 话 框 窗 体 均 适 用, 且 弥 补 了 宋 立
    波 先 生 文 章 中 所 述 封 面 关 闭 后 应 用 程 序 窗 体 才 能 显 示
    的 不 足 之 处。 方 法 如 下:
    ---- 1. 用App Wizard 生 成 一 工 程, 然 后 打 开 资 源 编 辑 器,
    Import 一 个BMP 图 形 文 件( 注: 由 于VC 的Bitmap 资 源 只 能 使
    用256 色 图 形, 所 以 需 用 画 板 或 其 他 图 形 工 具 转 换 成256
    色 图 形), 打 开 属 性 对 话 框, 将ID 号 改 为IDB_BITMAP -SPLASH。

    ---- 2. 新 建 一 个 对 话 框 资 源, 打 开 属 性 对 话 框, 选 择
    General; 将 新 对 话 框 资 源 的ID 号 改 为IDD_DIALOG_SPLASH, 选
    择Styles, 将Title bar 选 择 框 置 空, 改 变Border 属 性; 选 择
    None, 则 封 面 为 平 面 图 形; 选 择Dialog Frame, 则 封 面 为 立
    体 三 维 图 形。 在 对 话 框 资 源 上 添 加Picture 控 件, 打 开 属
    性 对 话 框 选 择General, 将Picture 控 件 的Type 改 为Bitmap、 Image
    改 为IDB -BITMAP -SPLASH; 将 对 话 框 资 源 的 大 小 改 为 与
    Picture 控 件 大 小 相 同。

    ---- 3. 选 对 话 框 资 源IDD_DIALOG_SPLASH, 打 开Class Wizard 添 加
    一 个 新 类, 类 名 定 义 为CSplash。

    ---- 4. 在MainFrame.h 的 类 定 义 中 添 加 一 指 针 成 员 变 量
    *sphash, 类 型 为CSplash; 在MainFrame.cpp 文 件 中 添 加 头 文 件,
    引 用 #include“splash.h”。 打 开Class Wizard, 选 择Message Maps,
    在Class name 中 选 择CMainFrame, 在Messages 中 选 择WM_TIMER 添 加
    OnTimer 函 数。

    ---- 5. 在CMainFrame 类 的OnCreate 函 数 中 添 加SetTimer 函 数, 并
    添 加 显 示splash 对 话 框 的 程 序 段:

    int CMainFrame::OnCreate
    (LPCREATESTRUCT 1pCreateStruct)
    {
    SetTime(1,50,NULL);

    ---- ∥ 添 加ID 号 为1 的WM_TIMER 事 件, 响 应 频 率 由 用 户 自 定 义, 但 不 宜 过 大

    splash=new CSplash();
    ∥ 添 加 播 放WAV 声 音 的 程 序 段
    ∥ 例 如:sndPlaySound(“sound.way,”SND_ASYNC);
    splash ->Create(IDD_DIALOG1);
    splash ->ShowWindo(SW_SHOW);
    splash ->UpdateWindow();
    … … ∥ 此 处 是OnCreate 函 数 中 原 有 的 程 序 段
    CenterWindow();
    ∥ 应 用 程 序 窗 体 居 中 显 示
    return 0;
    }

    ---- 6. 在CMainFrame 类 的OnTimer 函 数 中 添 加 响 应WM_TIMER 事 件
    的 程 序 段: Void CMainFrame::OnTimer(UINT nIDEvent)

    {
    ∥TODO:Add your message handler code here and/or call default
    if (nIDEvent==1)
    {
    if(splash ->Is WindowVisibl())
    {
    splash ->SetActiveWindow();
    ∥ 封 面 置 为 当 前 活 动 窗 体
    splash ->UpdateWindow();
    Sleep(2000); ∥ 封 面 显 示 停 留 时 间 由 用 户 自 己 改 变
    splash ->SendMessa(WM_CLOSE);
    }
    else
    {
    SetActiveWindow();
    ∥ 应 用 程 序 窗 体 置 为 当 前 活 动 窗 体
    KillTimer(1);
    ∥ 清 除WM_TIMER 事 件
    }
    }
    }

    ---- 到 此, 封 面 制 作 成 功, 如 果 要 添 加 声 音, 只 需 按 宋
    立 波 先 生 文 章 中 所 述 方 法, 添 加 在CMainFrame 类 的OnCreate
    函 数 中 注 释 段 内 即 可。 此 法 制 作 简 单, 容 易 实 现, 唯 一
    的 缺 点 是 此 封 面 显 示 的 图 形 色 彩 不 够 丰 富。( 贾 暾)
  • 很 多 大 型 应 用 程 序 都 有 启 动 封 面, 如Word 等 办 公 系 列 软
    件 和VC + + 等编 程 软 件。 通 过 启 动 封 面, 除 了 显 示 应 用 程
    序 名 称 和 版 权 等 提 示 画 面,还 可 避 免 由 于 应 用 程 序 启 动
    前 进 行 大 量 数 据 初 始 化 时, 用 户 较 长 时 间 的等 待, 给 应
    用 程 序 增 添 了 许 多 动 态 特 性 和 专 业 规 范。 鉴 于VC + + 开
    发 工 具 应 用 较 广, 这 里 以VC5 为 例 阐 述 启 动 封 面 的 一 般
    实 现 步 骤。

    ---- 1. 制 作 封 面 位 图

    ---- 制 作 应 用 程 序 启 动 封 面 真 彩 位 图, 记 录 位 图 的 高 度
    和 宽 度, 建 立 所 需要 的 其 他 声 音 等 文 件。

    ---- 2. 建 立 应 用 程 序

    ---- 利 用FILE
  • 随着Microsoft凭借Windows在操作系统上取得的巨大成绩,Windows用户界面也
    日益成为业界标准。统一的界面给广大用户对应用软件的学习与使用带来了很大
    方便。但每天都面对同一副面孔,日久天长难免会产生一些厌倦,开发一些"离
    经叛道",一改Windows应用程序千篇一律的"标准"界面,一定会给你带来一种清
    新的感觉。
    ---- 标准Windows应用程序窗口一般为带有标题栏的浅灰色矩形外观,因而"异
    形"对话框/窗口也主要是颜色与外形上动手脚。

    ---- 1.改变背景颜色

    ---- 改变对话框(窗口)的背景颜色是最简单的改变Windows应用程序外观的
    方法,根据Windows创建与管理机理,一般有两种方法。一种是处理WM_CTLCOLOR
    消息,首先创建所选背景颜色的刷子,然后调用SetBkColor()或
    SetDialogBkColor()以所创建的刷子来绘制窗口或对话框的背景。需要重画窗
    口或对话(或对话的子控件)时,Windows向对话发送消息WM_CTLCOLOR,应用程
    序处理WM_CTLCOLOR消息并返回一个用来绘画对话背景的刷子句柄。另外一种是
    响应Windows的WM_ERASEBKGND消息,Windows向窗口发送一个WM_ERASEBKGND消息
    通知该窗口擦除背景,可以使用VC++的ClassWizard重载该消息的缺省处理程序
    来擦除背景(实际是用刷子画),并返回TRUE以防止Windows擦除窗口。

    ---- 2.改变窗口外形

    ---- 通过使用新的SDK函数SetWindowRgn(),可以将绘画和鼠标消息限定在窗
    口的一个指定的区域,因此实际上是使窗口成为指定的不规则形状(区域形状)
    。"区域"是Windows GDI中一种强有力的机制,区域是设备上的一块空间,可以是
    任意形状,复杂的区域可以由各个小区域组合而成。Windows内含的区域创建函
    数有CreateRectRgn()、CreatePolyRgn()、CreatePolygonRgn()、
    CreateRoundRectRgn()和CreateEllipticRgn(),再通过CombineRgn()来组合
    区域,即可得到复杂形状的区域,获得复杂形状的窗口外形。

    ---- 通过上面的方法虽然可以得到"异形"窗口,但感觉颜色单调,外形也
    不够"COOL",能否获得更酷的"异形"对话框/窗口呢?回答是肯定的。下面就
    介绍利用位图和蒙板创建"异形"对话框/窗口的方法。

    ---- 3.利用位图创建异形对话框窗口

    ---- 利用位图创建异形对话框原理是根据象素的颜色来进行"扣像"处理,对所有
    非指定颜色象素区域进行区域组合。利用这一技术,实际上就是实现对话框/窗
    口的位图背景,并且对指定的颜色区域进行透明处理。

    ---- 下面就以透明位图为背景的对话框为例来说明:(略)......

    ---- 4.进一步的讨论

    ---- 前面实现了单一模式的异形对话框,但有些情况下又需要不同的样式,如
    有标题栏、边框等,或者只作局部的处理,这就是前面两个成员变量
    m_FrameWidth和m_CaptionHeight作用,通过在OnInitDialog()判断窗口样式,
    使m_FrameWidth和m_CaptionHeight取不同的值。这部分的代码为:......

    ---- 5.结束语

    ---- 这种异形窗口的创建不仅适应于对话框,而且适应于所有的基于CWnd类的
    派生窗口。采用这一方法,你可以创建出任何只要你能够画出的窗体,实现只要
    可以画出,就可以做出的目标。

    ---- 本文代码在Visual C++ 5.0、6.0下调试通过,运行正常,操作系统为
    Windows98SE。
  • 随着Windows95的推出,在PC系统中出现了越来越多的应用程序采用了非矩
    形外观的窗体,或者模拟现实中的事物,如钟、眼睛等;或者创造一个具有三维
    观感的非现实物体,这类程序以各种mp3播放器为代表,甚至一些大腕级的老牌
    应用程序(如Norton),也开始拥有这种窗口。一来是因为Windows操作系统和
    各类开发工具自身功能的极大提升;二来,也说明开发人员希望通过与众不同的
    外观,来强调使用时的第一映像,以期达到吸引用户的目的,毕竟,现在的PC是
    一个充满图形(图象)的世界,充分利用这一特点,也能在一定程度上改善程序
    界面的可操作性。而且,对于同一类型的应用程序,在功能、性能相差不大的情
    况下,用户也往往愿意选择外观漂亮的那种。因而,很多文章都在介绍如何创建
    不规则的窗口,但几乎千篇一律地基于VB进行说明。而笔者本人一直基于C/C++
    语言进行开发,因此,研究了一下在VC++5中实现不规则窗口的方法,下面就实
    现的主要方法进行说明。
      VC++5提供了CRgn类和SetWindowRgn()函数来实现不规则的程序窗口。创建
    一个不规则窗口的过程是:首先定义一个CRgn类,并用各种初始化函数创建CRgn
    类的具体区域,然后调用CWnd::SetWindowRgn()函数创建不规则窗口。
      CRgn是从CgdiObject衍生出来的类,用来确定一个多边形、椭圆或者由多边
    形及椭圆合成的范围,在程序中主要会用到CreateRectRgnIndirect()、
    CreateEllipticRgnIndirect()、CreatePolygonRgn()三个函数。
      CreateRectRgnIndirect(LPCRECT
    lpRect)函数创建一个矩形区域,参数lpRect指定所创建的矩形区域在窗口用户区
    中的left(左)、top(上)、right(右)、bottom(下)坐标。例如:
    CRgn MyRgn;
    RECT m_rect;
    m_rect.left=0; m_rect.top=0; m_rect.right=500; m_rect.bottom=300;
    MyRgn.CreateRectRgnIndirect( &m_rect );
      CreateEllipticRgnIndirect(LPCRECT
    lpRect)函数创建一个椭圆形区域,参数lpRect指定所创建的椭圆形区域在窗口
    用户区中的left(左)、top(上)、right(右)、bottom(下)坐标,如果指
    定right坐标与left坐标之差等于bottom坐标与top坐标之差,则创建的区域是一
    个圆。例如:
    CRgn MyRgn;
    RECT m_rect;
    m_rect.left=0; m_rect.top=0; m_rect.right=500; m_rect.bottom=300;
    MyRgn.CreateEllitpticRgnIndirect( &m_rect );
      CreatePolygonRgn(LPPOINT lpPoints, int nCount, int
    nMode)函数创建一个多边形区域,参数lpPoints指向一个POINT结构数组,
    在POINT结构数组中每个POINT结构项,用来确定多边形顶点在窗口用户区中的
    坐标;nCount说明POINT结构数组中POINT结构项的数目,也就是多边形的顶点
    数;nMode指定多边形的填充方式,一般使用ALTERNATE方式。例如创建一个三
    角形:
    CRgn MyRgn;
    POINT Points[3];
    Points[0].x=Points[0].y=0; Points[1].x=10; Points[1].y=30; Points[2].x=5; Points[2].y=60;
    MyRgn.CreatePolygonRgn(Points, 3, ALTERNATE);
      利用以上的函数创建区域后,就可以调用CWnd::SetWindowRgn(HRGN hRgn, BOOL
    bRedraw)来创建非矩形的窗口了。SetWindowRgn()函数参数说明:hRgn是一个CRgn类
    的句柄;bRedraw如果被设置成TRUE,那么,在窗口次序发生变化时,系统会发
    送WM_WINDOWPOSCHANGING和WM_WINDOWPOSCHANGED消息给窗口。
      如果要创建外形更复杂的窗口,例如mp3播放器Soniq的一个播放界面,就是
    两个圆形部分重合形成的。对于这类窗口的创建,还要用到CRgn类另外一个极其
    重要的函数——CombineRgn()。首先要说明的是:在VC++5的在线帮助中,将这个
    函数归入了初始化(Initialization)类型中,实际上,如果定义的CRgn类在没
    有使用其它初始化函数初始化之前,就调用这个函数的话,程序将会失败,所
    以,这个函数似乎应该归入operation类更恰当。
      CombineRgn(CRgn* pRgn1, CRgn* pRgn2, int
    nCombineMode)函数用来创建一个由多个多边形、椭圆合成的不规则区域。
    pRgn1、pRgn2分别指向参与合成不规则区域的多边形或椭圆形;nCombineMode说
    明合成的方式:RGN_AND最后的区域是pRgn1和pRgn2的重叠部分;RGN_DIFF最后
    的区域是pRgn1中不包含pRgn2的部分;RGN_OR最后的区域同时包含pRgn1和
    pRgn2;RGN_XOR最后的区域同时包含pRgn1和pRgn2,但不包含pRng1和pRng2重
    叠的部分。例如,创建一个类似Soniq播放器的界面:
    ......
    RECT m_Cyc1;
    RECT m_Cyc2;
    CRgn RgnCyc1;
    CRgn RgnCyc2;
    CRgn RgnDlg;
    m_Cyc1.left=100; m_Cyc1.top=5; m_Cyc1.right=200; m_Cyc1.bottom=105;
    m_Cyc2.left=80; m_Cyc2.top=85; m_Cyc2.right=180; m_Cyc2.bottom=185;
    RgnDlg.CreateEllipticRgnIndirect( &m_Cyc1 );
    RgnCyc1.CreateEllipticRgnIndirect( &m_Cyc1 );
    RgnCyc2.CreateEllipticRgnIndirect( &m_Cyc2 );
    RgnDlg.CombineRgn( &RgnCyc1, &RgnCyc2, RGN_OR );
    MyWin.SetWindowRgn( (HRGN)RgnDlg, TURE );
    ......
  • 作者:cvisual (贝贝)

    对话框概述
    一个通用对话框是由Windows设计和编写的,用于完成某一特定工作,比如打开
    一个文件或选择一种颜色等。MFC提供了管理每一通用对话框类型的类,这些类概
    括在下表中。表1 例如:处理File菜单上Open和Save As命令时使用CFileDialog
    通用对话框,而处理Prin和Print Setup命令时则使用CPrintDialog通用对话框。
    用这些对话框构造用户应用是很方便的,但有时这些通用对话框的外部特性和行
    为不能满足用户要求,特别是在对话框上需要增加某些控件或删除某些控件时尤
    为突出。本文提供给大家一种方法来定制通用对话框的外部特性和行为,可用自
    己的代码与MFC提供的类协同处理这些对话框,以满足用户的不同要求。一方面
    由于使用了MFC的通用对话框类,节省了大量的代码编程工作,提高了开发效率;
    另一方面,采用自己的代码协同实现,使对话框的功能可以做到灵活多样。本文
    以F ile Open命令为例,来阐述这一方法。
    定制File Open通用对话框
    CFileDialog类封装Windows公共文件对话框。公共文件对话框以一种与Windows
    标准一致的方式,提供了实现File Open和File Save A s对话框(及其他文件选择
    对话框)的简单方法。
    CFileDialog中有一成员变量m_ofn,它是OPENFILENAME类型的结构。通常,要使用
    一个F ileDialog对象,需要首先用CFileDialog的构造函数创建该对象。在对话
    框被构造之后,且在用DoModal成员函数显示对话框之前,应用程序可以通过设置
    m_ofn结构中的值,来对此对话框中的控件的值或状态进行初始化。例如:应用程
    序可将m_ofn的lpsz Title成员设置为所想要的对话框标题;可将m_ofn的指定该
    对话框的创建标志Flags成员设置为其各标志位值的多种组合,以达到一定要求
    ,其中,设置的Flags中的OFN—ALLOWMULTISELECT标志可允许用户选择多个文件;
    设置OFN_HIDEREADONLY标志可隐藏Read Only检查框;设置O FN_OVERWRITEPROMT
    标志可做到若所选择的文件已存在,则使Save As 对话框产生一个消息框,使用户
    确认是否重写该文件;等等。有关此结构的详细说明,包括它的成员列表,参看
    Windows SDK文档中OPENILE N AME的介绍。
    从以上所述看,仅改变m_ofn的值只能做到简单的定制,若要对通用对话框的外观、
    通用对话框已有控件进行进一步调整(功能改变或删除),以及给通用对话框增加新
    的控制,则以上简单定制方法是做不到的。笔者将为大家提供一种方法,下面以一
    例子来介绍。本例假定一应用程序要读入某一位图文件,要求对要读入的位图文
    件进行预览,具体实现是在File Open通用对话框的基础上增加预显功能,即每当
    选中一个位图文件时,就在对话框中显示此文件的位图。为节省编程工作量,可
    对Windows提供的File Open通用对话框进行定制。本例定制对话框采取的方案
    是直接使用CFileDialog类,设法获取标准通用对话框资源并对其进行修改,然后
    编写File Open菜单命令处理程序和一个钩子函数(HOOK)来处理特殊要求。本例
    在VC++4.0环境中创建一个DibPreView项目来实现。
    1.获取并改造通用对话框资源
    在使用VC++自动创建的应用程序中,系统菜单File项中的Open项的处理是调用了
    系统由CommDll.DLL提供的标准File Open对话框,要实现对其定制就必须截获
    Open项的命令,用自己的代码来处理,以替代系统提供的缺省处理。要做到这点并
    不困难,只要用ClassWizard在合适的对象(比如文档类或视窗类等)中增加对
    ID_FILE-OPEN控制的处理 ,即增加OnFileOpen函数,编写此函数就能实现对Open
    命令的处理。在此函数中首先要创建一个CFileDialog类的对象,该对象的外观和
    行为均同标准的File Open对话框相同,要改变该对象的外形或删除原对话框中的
    某些控件或增加某些控件,都必须首先设法获取该对象所对应的标准资源,然后,
    对其进行相应的修改。获取资源的方法有两种:
    方法一:
    在Msdev/Include目录中存有以上几个标准通用对话框的资源,以 FileOpen.Dlg、
    FotD lg、FindText.Dlg、Color.Dlg和PrnSetup.Dl g文件方式存在,这些文件
    其中的一些常量被定义在本目录中的dlgs.h 中。为了定制该对话框,就要以显式
    方式引用这些资源,要将它们嵌入到本应用程序的资源中,然后再修改该对话框的
    资源。具体步骤如下:
    (1)用VC++4.0自动创建项目DibPreView的全部文件;
    (2)以Text方式打开本项目中的资源文件DibPreView.rc,在此文件中的Dialog段
    首行前加上语句:
    #include <FileOpen.dlg>
    (3)将Afxres.h文件从Msdev/Mfc/Include目录中复制到本项目目录中,并打开新
    复制的Afxres.h文件,将下面的语句添加到文件行首:
    #include <dlgs.h>
    方法二:
    在VC++安装盘中的Msdev/Sample/Mfc/General/Clipart目录中存有资源文件
    Commdlg.c ,此文件含有Color、File、Open(或Save As) 、Find、Font、Print、
    Print Setup、Replce 几个通用对话框的资源,为了以显式方式引用这些资源,可
    利用VC++的资源编辑器,将所需的通用对话框资源复制到本例项目资源文件中。

    按以上两方法处理完后,在本项目的资源目录列表中的Dialog项中,出现一标识
  • 用VC++6.0制作图片屏幕保护程序

      VC++可谓神通广大,如果学到家了,或者就掌握了那么一点MFC,你也会
    感到它的方便快捷,当然最重要的是功能强大。不是吗,从最基本的应用程
    序.EXE到动态连接库DLL,再由风靡网上的ActiveX控件到Internet Server API,
    当然,还有数据库应用程序……瞧,我都用它来做屏幕保护程序了。一般的屏幕
    保护程序都是以SCR作为扩展名,并且要放在c:/windows 目录或
    c:/windows/system 目录下,由Windows 98内部程序调用(Windows NT 是
    在 c:/windows/system32 目录下)。怎么调用?不用说了,这谁不知道。
      好了,我们来作一个简单的。选择MFC AppWizard(exe),Project Name 为
    MyScreensaver,[NEXT],对话框,再后面随你了。打开菜单Project、
    Settings,在Debug页、Executable for debug session项,以及Link页
    中Output file name项改为c:/windows/MyScreensaver.scr,这样,你可以
    调试完后,直接在VC中运行(Ctrl+F5),便可看到结果。当然,这样做的
    唯一缺点是你必须手动清除Windows 目录下的垃圾文件(当然是在看到满意结
    果后;还有,你可借助SafeClean 这个小东东来帮你清除,除非你的硬盘大的
    让你感到无所谓……快快快回来,看我跑到那里去了)。接下来用Class Wizard
    生成CMyWnd类,其基类为CWnd(在Base Class 中为generic CWnd)。这个类是我
    们所要重点研究的。创建满屏窗口、计时器,隐藏鼠标,展示图片,响应键盘、
    鼠标等等,这家伙全包了。至于MyScreensaverDlg.h与MyScreensaverDlg.cpp文
    件我们暂时不管。打开MyScreensaver.cpp,修改InitInstance()函数:
      BOOL CMyScreensaverApp::InitInstance()
      {
       AfxEnableControlContainer();
      #ifdef _AFXDLL
       Enable3dControls(); // Call this when using MFC in a shared DLL
      #else
       Enable3dControlsStatic(); // Call this when linking to MFC statically
      #endif
       CMyWnd* pWnd = new CMyWnd;
       pWnd->Create();
       m_pMainWnd = pWnd;
       return TRUE;
      }
      当然,再这之前得先 #include “MyWnd.h" 。后面要做的都在MyWnd.h
    与 MyWnd.cpp 两文件中了。
      下面给出CMyWnd 的说明:
      class CMyWnd : public CWnd
      {
      public:
       CMyWnd();
       static LPCSTR lpszClassName; //注册类名
      public:
       BOOL Create();
      public:
       // ClassWizard generated virtual function overrides
       //{{AFX_VIRTUAL(CMyWnd)
       protected:
       virtual void PostNcDestroy();
       //}}AFX_VIRTUAL
      public:
       virtual ~CMyWnd();
      protected:
       CPoint m_prePoint; //检测鼠标移动
       void DrawBitmap(CDC& dc, int nIndexBit);
       //{{AFX_MSG(CMyWnd)
       afx_msg void OnPaint();
       afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
       afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
       afx_msg void OnMButtonDown(UINT nFlags, CPoint point);
       afx_msg void OnMouseMove(UINT nFlags, CPoint point);
       afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
       afx_msg void OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
       afx_msg void OnDestroy();
       afx_msg void OnTimer(UINT nIDEvent);
       afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
       afx_msg void OnActivateApp(BOOL bActive, HTASK hTask);
       //}}AFX_MSG
       DECLARE_MESSAGE_MAP()
      };
      MyWnd.cpp 文件:
      ……
      CMyWnd::CMyWnd()
      {
       m_prePoint=CPoint(-1, -1);
      }
      LPCSTR CMyWnd::lpszClassName=NULL;
      BOOL CMyWnd::Create()
      {
       if(lpszClassName==NULL)
       {
       lpszClassName=AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW,
      ::LoadCursor(AfxGetResourceHandle(),MAKEINTRESOURCE(IDC_NOCURSOR)));
      //注册类;IDC_NOCURSOR为新建光标的ID,这个光标没有任何图案
       }
       CRect rect(0, 0, ::GetSystemMetrics(SM_CXSCREEN),
       ::GetSystemMetrics(SM_CYSCREEN));
       CreateEx(WS_EX_TOPMOST, lpszClassName, _T(“”), WS_VISIBLE|WS_POPUP,
       rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
       GetSafeHwnd(), NULL, NULL); //创建一个全屏窗口
       SetTimer(ID_TIMER, 500, NULL);//计时器,ID_TIMER别忘了定义
       return TRUE;
      }
      为了防止同时运行两个相同的程序,下面两个函数是必需的:
      void CMyWnd::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
      {
       CWnd::OnAc
  • ---- Visual C++ 是 一 种 功 能 十 分 强 大 的 程 序 设 计 语 言, 利
    用 它 可 以 很 方 便。 快 捷 的 开 发 出Windows 下 的 各 种 应 用 程
    序。 尤 其 是 其 内 置 了 数 据 库 的 接 口, 可 使 我 们 毫 不 费 力
    地 操 作Accse,FoxBASE+ 等 许 多 数 据 库 文 件。 但 在 安 全 性 上,
    由 于FoxBASE+ 是 一 种 早 期 的 产 品, 所 以, 不 能 像Accse 那 样
    为 数 据 库 文 件 本 身 加 密, 这 就 可 以 让 入 侵 者 直 接 对 数
    据 库 操 作 而 导 致 数 据 的 损 失。

    ---- 本 文 提 供 的 方 法 可 以 做 为 一 个 小 的 模 块 放 到 你 的 应
    用 程 序 中 调 用, 实 现 数 据 库 的 加 解 密。

    ---- 首 先, 打 开Visual C++ 5.0 选 择New 建 立 一 个 基 于 对 话 框
    的 应 用 程 序, 然 后 在 对 话 框 上 面 放 置 两 个 按 钮, 其 中 一
    个 标 题 叫 做 加 密, 另 一 个 标 题 叫 做 解 密。 对 应 实 现 如 下:

    void CAa6Dlg::OnButton1() (对应于加密按钮的单击)
    {
    int buf2[512],tcd,tcd1,*p1,i;
    CFile cfile1;
    char *pfilename;
    pfilename="c://aa.dbf";
    cfile1.Open(pfilename,CFile::modeReadWrite);
    cfile1.Read(buf2,32);
    p1=(int*)buf2;
    p1=p1+1;
    tcd=*p1;
    tcd1=(tcd-1)/2;
    cfile1.SeekToBegin();
    cfile1.Read(buf2,tcd);
    buf2[0]=buf2[0]+0x0017;
    for (i=16;i

    ---- 这 样, 用 本 程 序 的 加 密 功 能 加 密 的Fox 系 列 数 据 库
    ( 如Foxbase+, Foxpro 等), 用Foxbase+,Foxpro 等 数 据 库 软 件 无
    法 对 其 进 行 读 写。 只 有 经 过 解 密 之 后, 才 能 被 其 识 别。
  • ---- 比 较 复 杂 的 数 据 库 中 一 般 会 有 位 图 数 据( 比 如 相 片)
    。 虽 然 这 类“OLE 对 象” 的 插 入、 删 除 以 及 替 换 操
    作 在ACCESS 里 容 易 实 现, 在VC 中 却 显 得 复 杂 而 且 颇 费 周 折。
    以 下 把 作 者 用VC 处 理ACCESS 数 据 库 中 的 位 图 数 据 的 体 会
    简 单 叙 述 一 下, 以 请 教 于 大 家。

    ---- 在 CdaoRecordset 派 生 类 的 对 象 中,VC 自 动 为ACCESS 的
    “OLE 对 象” 域 生 成 一 个CLongBinary 对 象。 该 类 虽
    然 较 简 单, 在 程 序 里 却 需 要 使 用 全 局 函 数GlobalAlloc() 和
    GlobalFree() 处 理 与 它 的 内 存 句 柄m_hData 有 关 操 作, 访 问 数
    据 前 后 要 调 用GlobalLock() 和GlobalUnlock(), 而 且 还 要 给 它
    的m_dwDataLength 赋 值, 使 用 起 来 相 对 复 杂, 所 以 一 般 推 荐
    使 用CByteArray 类。 这 只 需 要 在CdaoRecordset 派 生 类 对 象 的 数
    据 说 明 里 修 改 一 下, 并 把DoFieldExchange() 里 的
    DFX_LongBinary() 改 成DFX_Binary() 即 可。

    ---- 作 者 定 义 了 一 个 以CObject 为 基 类 的CDib 类
    (CDaoRecordView 的 派 生 类 里 定 义 了CDib 对 象 成 员m_DIB), 其
    中 包 括 一 下 成 员 和 方 法:

    CByteArray m_bufDIB;
    BOOL Create(CByteArray& ba);
    BOOL Create(CFile& bmpFile);
    BOOL Paint(HDC hDC);

    ---- m_bufDIB 是 存 储 位 图 数 据 的 缓 冲 区。 为 简 便 起 见, 它
    不 包 含 包 装 信 息 和BITMAPFILEHEADER 结 构。 这 样 对 数 据 库 更
    新 后, 原 有 的“OLE 对 象” 类 型 将 变 成“ 长 二
    进 制 数 据”, 不 能 在ACCESS 里 查 看 了。

    ---- 第 一 个Create() 重 载 方 法 的 参 数ba 是 记 录 集 的 位 图 数
    据( 比 如m_image), 使 用CByteArray::Copy() 把 数 据 复 制 给
    m_bufDIB; 第 二 个Create() 方 法 的 参 数bmpFile 是 已 打 开 的 位
    图 文 件, 使 用CFile::ReadHuge() 把 文 件 里 的 数 据 读 入m_bufDIB
    ( 放 弃 前 面 的BITMAPFILEHEADER 结 构):

    DWORD dwBufSize;
    dwBufSize = bmpFile.GetLength();// 获 得 文 件 长 度
    bmpFile.Seek((long)sizeof(BITMAPFILEHEADER),
    CFile::begin);// 放 弃 文 件 头
    dwBufSize-=sizeof(BITMAPFILEHEADER);
    m_bufDIB.SetSize(dwBufSize );// 设 置 缓 冲 区 大 小
    file.ReadHuge((LPSTR)(m_bufDIB.GetData()), dwBufSize);
    ……

    ---- Paint() 方 法 调 用 了SetDIBitsToDevice() 函 数( 根 据 情 况 也
    可 以 使 用StretchDIBits ()), 参 数hDC 是CDaoRecordview 的 资 源 中
    的 一 个 静 态 控 制 的 设 备 句 柄, 作 为SetDIBitsToDevice() 的 第
    一 个 参 数。 如 果 不 是16 或24 位 的 位 图, 还 需 要 建 立 和 设 置
    调 色 板。Paint() 方 法 除 了 在 CDaoRecordView 派 生 类 的OnMove() 里
    调 用 外, 也 被OnPaint() 调 用( 最 好 不 在OnDraw() 里 调 用):


    void CDerivedView::OnPaint()
    {
    CPaintDC dc(this);
    CClientDC dc1(&m_ctlImage);
    if(m_DIB.Create(m_pSet->m_image))
    m_DIB.Paint(dc1.m_hDC);
    }

    ---- 作 者 首 先 采 用 的 方 法 是, 每 当 打 开 一 个 位 图 文 件,
    调 用m_DIB.Create() 和m_DIB.Paint(), 然 后 复 制 给m_pSet->m_image,
    再 设 置“ 脏” 标 识:

    if(m_DIB.Create(bmpFile))
    {
    CClientDC dc(&m_ctlImage);
    m_DIB.Paint(dc.m_hDC);
    (m_pSet->m_image).Copy(m_DIB.m_bufDIB);
    SetFieldDirty(&(m_pSet->m_image));
    }

    ---- 记 录 滚 动 时,OnMove() 调 用Update() 对 数 据 进 行 更 新。

    ---- 但 是 这 样 做 的 结 果 是, 只 有 在 域 的 内 容 不 为 空(NULL)
    的 时 候 才 能 更 新 数 据。 也 就 是 说, 添 加“ 长 二 进 制
    数 据” 不 能 实 现。

    ---- 最 后 发 现 使 用SeieldValue() 可 以 实 现 添 加 和 替 换。 但
    由 于 作 者 未 知 的 原 因, 还 需 要 把 另 外 某 个 域 设 置 为
    “ 脏” 才 行:

    if(m_DIB.Create(bmpFile))
    {
    CClientDC dc(&m_ctlImage);
    m_DIB.Paint(dc.m_hDC);
    (m_pSet->m_image).Copy(m_DIB.m_bufDIB);
    // 只 为OnPaint() 调 用 时 使 用
    m_pSet->SetFieldValue(_T("[image]"),
    COleVariant(m_DIB.m_bufDIB));
    m_pSet->SetFieldDirty(&(m_pSet->m_name));
    // 任 意 另 外 一 个 域
    }

    ---- 如 果 打 算 删 除 数 据 库 里 的 位 图 数 据, 可 以 把 一
    个“ 空” 的CByteArray 对 象 替 换 原 来 的 就 行 了。
  • 问:我正在试着用MFC来制作弹出窗口,我看过一些关于建立弹出窗口的文章,
    它们是使用 CWnd对象的。但在文档,视窗结构中是怎样实现的?


    答:你可以建立一个非模态对话框(使用Create函数),你可以在任何建立窗
    口,子窗口等。 如果你一定要在文档、视窗结构中实现,你也可以用
    CCreateContest类。 下面是建立MDI窗口的例子:


    {
    LPCTSTR lpszClassName = NULL;
    CCreateContext cContext;

    cContext.m_pNewViewClass = RUNTIME_CLASS ( CMyView )

    DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW &
    ~WS_THICKFRAME & ~WS_MAXIMIZEBOX;

    // TODO: Add your specialized code here and/or call the base class

    if ( CMDIChildWnd::Create(lpszClassName, lpszWindowName, dwStyle,
    pParentWnd->rectDefault, pParentWnd, &cContext) )
    {
    InitialUpdateFrame ( NULL, TRUE );
    CScrollView *pView = ( CScrollView* ) GetActiveView();

    if ( pView )
    pView->ResizeParentToFit ( FALSE );

    return TRUE;
    }
    else
    return FALSE;
    }
    CCreateContext有一个成员为m_pCurrentDoc,你可以用它来将一个文档分配到相应的窗口上.
  • 现代显示器的很多优点,如长寿命的显示屏,液晶和能源之星能源保护模式, 已

    经让屏幕保护程序的作用大大地降低. 但是,依然有大量的屏幕保护程序出现,

    尤其是共享软件.
    这也许是因为写屏幕保护程序是一件非常有趣的事. 因为有CRect和CGdiObject

    类,这些类的绘图功能比单纯的C API函数容易得多, 所以,用MFC写屏幕保护程

    序会是一件更有趣的事.
    简单地和"Hello, world."应用程序比较,它不需要WinMain()函数,例如:
    如果你发掘一下有哪些API函数支持产生一个屏幕保护程序,你会发现可利用的

    函数非常少. 例如:用C写屏幕保护程序,大多数情况下都不需要调用

    DefWindowProc函数,取而代之的是DefScreenSaverProc函数. 如果调用你自已

    的函数(一般也就是三个),你可以编出一个和标准的屏幕保护程序功能一样的屏

    保程序.
    对所有的屏幕保护程序:
    屏幕保护程序的名字和描述在字符串1中定义.
    屏幕保护程序的图标为ID_APP, 在中定义为100.
    WINAPI函数ScreenSaverProc必须定义和调用.
    (CScreenSaverWnd模块为你填充这个API调用.)
    程序必需以.SCR为扩展名.
    对于可设置的屏幕保护程序:
    设置屏幕保护程序的对话框为DLG_SCRNSAVECONFIGURE,在定义为2003.
    WINAPI函数ScreenSaverConfigureDialog必须被定义和调用.
    WINAPI函数RegisterDialogClasses必须被定义和调用.
    (CScreenSaverDlg模块为你填充这个API调用.)

    ---------------------------------------------------------------------

    ---

    某些MFC外部特性
    所有这些简单的特性来源于MFC,以及它便利的应用程序框架. 一个屏幕保护程

    序并不真的是一个应用程序,它只不过是操作系统在你离开键盘后才调用的一段

    代码. 它甚至不需要WINMAIN函数,MFC程序似乎不可能完成这一点,因为它已经

    调用了WINMAIN函数. 如果你没有用过WINAPI来编写C程序,你可能不知道MFC已

    经在幕后调用了DefWindowProc过程.
    你当然能用MFC来生成一个屏幕保护程序,对于上述的限制,你只要在MFC的基础

    上做一点点工作即可. 以下提供两个抽象类 CScreenSaverWnd和

    CScreenSaverDlg,它会考虑这种限制,并且让你很容易创建一个功能完全的屏幕

    保护程序.
    建一个基于对话框的MFC应用程序.
    使用VC4.2或5.0为屏幕保护程序产生一个新工程,你可以使用开发环境提供的

    AppWizard, 建立一个新"Win32 MFC Application"程序.如果你选择链接时MFC

    为共享(linked with MFC in a shared DLL), 屏幕保护程序会小很多.当然,基

    于对话框的应用程序将会避免产生不需要用DOC/VIEW构架.
    删除所以关于CWinApp的引用和它本身.
    删除所有CWinApp派生类的申明和定义,包括一个全局的instance.
    产生一个CWnd的派生类.
    我们已经创建了一个基于对话框的应用程序,但是屏幕保护程序只是需要一个简

    单的CWnd派生类. 你可以使用ClassWizard来产生一个继承于 generic CWnd

    class的派生类.
    选择父类.
    从下载的文件中拷贝CScreenSaverWnd和CScreenSaverDlg的相关文件,*.CPP和

    *.H (作者要求你能保留源代码中的版权信息). 在你的窗口类中查找CWnd,将其

    换成CScreenSaverWnd,将CDialog换为CScreenSaveDlg. 然后重新编译.
    一个特定的对话框.
    用ClassWizard产生的CDialog的派生类,没有处理命令行参数的构造函数. 因为

    屏幕保护程序的设置部分是一个窗口,需要命令行设置, 因此,在此提供了一个

    可以使用命令行的构造和析构函数.
    全局考虑.
    当删掉CWinApp的派生类对象时,也同时删掉了全局的instance, 因此,程序中

    CScreenSaverWnd的派生类需要有一个全局的instance. 同样,在

    CScreenSaverDlg的派生类中也要保留一个副本.
    资源.
    如前所述,屏幕保护程序包含以下资源: 字符串1中的描述,不要超过20个字符,

    当用户选择屏幕保护程序时,在下拉框中就会出现这个字符串.将图标资源的ID

    改为100.将对话框资源的ID改为2003.
    泡沫,清洗,重复.
    你已经做好一个框架,现在可以编译,调试和开发了. 你可以改变工程输出的文

    件扩展名为.SCR,从而能出现屏幕保护程序的设置对话框. 如果你想调试屏幕保

    护程序,在运行时你可以用命令行参数:"/save"
    分析自带的示例.
    示例使用VC5.0,但应该兼容于VC4.2,展示了 CScreenSaverWnd和

    CScreenSaverDlg的用法, 并且使用了CImageList来调用一个图标库,在屏幕上

    产生动画,请查看源程序的注解.

    ---------------------------------------------------------------------

    ---

    代码
    虽然CScreenSaverWnd不是CView的派生类,我觉得应该重载OnInitialUpdate和

    OnDraw.我也加入了三个特性,你可以使用,也可以不使用.
    CScreenSaverWnd的默认状态是黑屏,这由函数OnEraseBkgnd()来完成,你可以在

    构造函数,OnCreate,OnInitalUpadte这三个地方的任一处调用

    SetAutoBlack(FALSE)来关掉该项.
    成员变量m_pPalette指向CPalette,将被用于OnDraw调用之前的调色板设置, 重

    载OnQueryNewPalette()和OnPaletteChanged(
  • 现在有不少的软件都有这样的一种界面效果:当用户单击某一个按钮之后,并不
    是简单地执行某种功能或弹出一个对话框,而是在按钮旁边弹出一个菜单,让用
    户作更详细地选择,这在某种程度上就代替了简单的对话框,而且较对话框更
    为"用户友好"。这样的按钮基本上有两种类型:在按钮上显示文字的和在按钮
    上显示箭头的,显示箭头常见的有向右的和向下的两种,还有向上的和向左的。
    图示为常见的风格,即向下的箭头和在按钮左下角弹出菜单。那么,我们在编
    程时如何实现这一功能呢?
    ---- 我们知道,MFC中的CButton类有一个虚函数名叫DrawItem(),若在对话框
    模板中为控件指定了BS_OWNERDRAW风格,则在运行时将调用这个函数来画按钮,
    而CMenu类的成员函数TrackPopupMenu()则可以在屏幕的任何位置弹出菜单。由
    上得到启发,只要我们合理地使用这两个函数,就能创建出"菜单按钮"来。

    ---- 下面的CMenuButton类封装了全部的这些功能,让我们先来看一下它的制作
    原理。

    ---- 在取得了按钮的矩形区域之后,取其一个角落的值传递给
    TrackPopupMenu()函数即可实现弹出菜单,在TrackPopupMenu内部使用
    TPM_RETURNCMD标志可以得到用户选择的菜单的命令ID,以供进一步的处理;
    在重载了DrawItem()函数之后,我们可以在函数的内部使用
    CDC::DrawFrameControl()函数来画出基本的按钮外观,再在中间部位画一个箭
    头即可。箭头可以用Marlett字体来画。也许有人会担心,若果其他人的机器没
    装Marlett字体怎么办?其实,任何一台安装Windows的机器离开了Marlett字体
    都无法正常工作,先请看下图,这是Windows"系统工具"中自带的"字符映射表"。


    ---- 看到最上面一行中的那几个箭头了吗?就是要把它们画在按钮上。等一等,
    另外的几个符号怎么也那么熟悉?这不就是几乎每个窗口上都有的"最小化"、
    "还原"、"关闭"和"最大化"按钮吗?不错,Windows正是使用这几个字符在标题
    栏上绘图的。其实,Windows中的最"标准"的画箭头的方法就是使用Marlett字
    体,无论是工具栏上的箭头还是组合框中的箭头,都是这样画出的。有时,在乱
    删了字体之后,组合框或工具栏的下拉箭头会变成数字6或者9,为什么?看到
    状态栏上的"击键值"了吗?--"6",往右数,那个小一点的下箭头正好是--"9"。


    ---- 下面是具体的制作过程。

    ---- 首先,生成一个MFC AppWizard EXE 工程,最好是基于对话框的工程,当
    然,利用现有的工程也可以。生成一个以CButton为基类的新类,名为
    CMenuButton,然后用ClassWizard为其添加两个成员函数:DrawItem()
    和PreSubclassWidnow();手工为CMenuButton类添加BOOL类型m_bDrawFocusRect
    成员变量,用于决定是否在按钮上画焦点矩形,添加SetDrawFocusRect()函数用
    于设置这个标志,默认为画焦点矩形;添加两个枚举类型的变量m_ArrowType和
    m_PopupPos,用于决定所画的箭头的类型和菜单弹出的位置。箭头可为右箭头、
    下箭头、小右箭头、小下箭头、上箭头和左箭头(参见本文开始处的图);菜单
    的弹出位置可以为按钮的左上角、右上角、左下角和右下角。最后手工添加两
    个函数,SetArrowType()和SetMenuPopupPos(),用于设置以上各种风格,其默
    值分别为画右箭头和在左下角弹出。如果只需要菜单而不需要画箭头,只需置空
    BS_OWNERDRAW标志位即可,添加一个SetStyle()函数,用于设置是画箭头还是显
    示文本,其默认值是画箭头。 为方便处理按钮的BN_CLICKED通知消息,为
    CMenuButton类创建一个公有的成员函数OnClick(),以便在BN_CLICKED的消息处
    理器中调用。它有两个参数,第一个是菜单资源的ID,第二个参数为子菜单的
    ID,默认为0。如果只有一组子菜单,则可使用其默认值0。OnClick()函数的
    返回值为所选的菜单项的命令ID,若未作任何有效选择,则返回0。
  • 黄 晓 润

    ---- 在 数 据 库 应 用 时, 有 时 会 遇 到 这 样 的 情 况: 记 录 的 某 项
    信 息 由 变 长 数 组 构 成。 这 时 传 统 方 法 是 建 立 一 个 新 表, 并 通
    过 表 间 的 关 系 来 记 录 将 相 应 数 据 关 联 起 来; 但 是 这 样 实 现 相
    对 比 较 复 杂, 而 且 也 不 符 合 思 维 逻 辑。 由 于 该 数 组 整 个 构 成
    记 录 的 一 个 具 体 项, 因 此 如 果 能 够 将 它 作 为 一 个 字 段 则 更 加
    直 观 些, 同 时 在 一 些 情 况 下 也 比 较 容 易 处 理。 那 么, 下 面 将
    向 您 提 供 一 种 这 样 的 方 法。 为 了 说 明 上 的 方 便, 我 首 先 假 定
    了 下 面 的 需 求:
    ---- 在 个 人 简 历 数 据 表 中, 需 要 将 工 作 经 历 分 为 起、 止 时 间、
    单 位、 职 位 等 信 息。

    ---- 在 这 种 情 况, 可 以 分 别 建 立 个 人 简 历 和 工 作 经 历 两 个 表
    并 将 之 通 过 关 键 字 进 行 关 联, 从 而 达 到 要 求。 但 也 可 以 采 用
    只 建 立 一 个 表 的 方 式 来 完 成 数 据 的 管 理,

    ---- 那 么 如 何 完 成 呢 ? 下 面 将 给 出 具 体 的 实 现。

    ---- 首 先 建 立 一 个 表Person, 记 录 包 含 个 人 简 历 需 要 的 各 个 字
    段, 将 工 作 经 历 作 为 一 个 字 段, 其 类 型 为Image( 在Access 数 据 库
    中 为"OLE 对 象")。 下 面 新 建 类CPersonRs。 将 它 与 表Person 对 应 起
    来, 并 将 m_dResume 对 应 到 字 段 工 作 经 历 中,m_dResume 的 类 型 为
    CByteArray 。 下 面 我 们 来 定 义 工 作 定 义 的 基 本 结 构:

    class CResumeItem
    {
    public:
    COleDateTime m_timeBegin;
    COleDateTime m_timeEnd;
    COleDateTime m_strCompany;
    COleDateTime m_strTitle;

    Void Serialize( CArchive& ar )
    };
    typedef CArray< CResumeItem, CResumeItem& > CResume;

    ---- 在CPersonRs 中 定 义 下 面 的 函 数

    CPersonRs
    {
    ……
    CByteArray m_dResume;
    ……
    void SetResume( CString strName, CResume& resume );
    void GetResume( CString strName, CResume& resume );
    };

    并在相应的文件给出相应的函数的实现;
    void CResumeItem::Serialize( CArchive& ar )
    {
    if ( ar.IsLoading() )
    {
    ar > > m_timeBegin
    > > m_timeEnd
    > > m_strCompany
    > > m_strTitle;
    }
    else
    {
    ar < < m_timeBegin
    < < m_timeEnd
    < < m_strCompany
    < < m_strTitle;
    }
    }
    函数SetResume和GetResume实现如下:
    void CPersonRs::SetResume
    ( CString strName, CResume& resume )
    {
    …………//根据strName定位到相应的记录
    Edit();
    CMemFile memFile;
    CArchive ar( &memFile, CArchive::store );
    Resume.Serialize( ar );
    ar.Close();
    DWORD dwSize = memFile.GetLength();
    LPBYTE lpInfo = memFile.Detach( );
    m_dResume.SetSize( dwSize );
    memcpy( m_dResume.GetData(),lpInfo,dwSize);
    SetFieldNull( &m_dResume, FALSE );
    SetFieldDirty( &m_dResume );
    ASSERT( CanUpdate() );
    Update( );
    free( lpInfo );
    }

    void CPersonRs::GetResume
    ( CString strName, CResume& resume )
    {
    …………//根据strName定位到相应的记录
    LPBYTE lpInfo;
    DWORD dwSize;
    dwSize = m_dResume.GetSize();
    lpInfo = m_dResume.GetData();
    memFile.Attach( lpInfo, dwSize );
    CArchive ar( &memFile,CArchive::load );
    resume.Serialize( ar );
    ar.Close();
    memFile.Detach( );
    }

    ---- 通 过SetResume 和GetResume 可 以 方 便 地 将Resume 的 内 容 读 出 或 写
    入 到 记 录 的 工 作 经 历 字 段 中, 而 且 更 新 也 相 当 方 便。 这 样 是
    就 可 以 在 工 作 经 历 字 段 任 意 个 工 作 经 历 项, 而 且 由 于 是 在 一
    个 表, 也 省 去 了 多 表 造 成 的 麻 烦。

    ---- 不 过, 最 后 需 要 声 明 的 是, 上 述 例 子 的 情 况 根 据 实 际 需
    要 可 能 用 多 表, 但 这 里 只 是 借 它 说 明 这 样 一 种 实 现 方 法, 也
    许 你 会 在 某 些 情 况 下 这 些 会 相 当 简 单 些。 本 文 也 只 给 出 实 现
    的 关 键 代 码, 对 于 如 何 使 用MFC 访 问 数 据 库、 序 列 化 机 制 可 以
    参 见VC 的 相 应 文 档。
  • 齐玉东 李逸波
    传统数据库系统缺乏知识,只能处理静态数据;而专家系统的狭窄
    应用领域及不能访问现存数据库,又防碍了专家系统的有效应用。数
    据库和人工智能这两个领域单独发展的局限性,促使了两者取长补短,
    共同发展。这就是专家数据库EDS(Expert Database Syst em)产生和
    发展的原因。通常,我们把既具有数据库管理功能及演绎能力、又提
    供专家系统中若干良好性能的数据库系统,称为专家数据库。EDS的基
    本思想是把以知识表达和知识处理为主的专家系统ES(Expert System
    )技术引进传统数据库,使二者有机结合,以开发出能共享信息的面向
    知识处理的问题求解系统。目前,EDS主要采用系统耦合--"紧耦合"
    及"松耦合"来实现。紧耦合指将规则管理系统集成到DBMS之中,使DBM
    S既管理数据库又管理规则库。这种方法实现难度较大。而松耦合是
    指将一个现成的专家系统外壳和一个现成的DBMS作为两个独立的子系
    统结合在一起,它们分别管理规则库和数据库。采取松耦合实现策略
    可以充分发挥原有两个系统的全部功能,而不需对原系统进行任何改
    动。它只需设计一个连接ES/DBMS的高效、灵活的接口模块,以协调二
    者的工作,所以实现起来时间短、见效快。
    一、故障诊断专家系统的系统结构
    在故障诊断系统HF-2000的研制中,我们采取松耦合策略建立了一
    个故障诊断专家数据库系统。该系统是一个产生式系统,采用深度优
    先策略作为其控制策略。系统根植于W indows平台,采用了面向对象
    的程序设计技术及先进的数据库技术;在数据库端,我们采用了基于Se
    rver/Client机制的MS SQL的数据库技术。在推理控制端,利用Visual
    C++进行编程,实现了一个推理机。推理机与数据库之间的接口则通
    过ODBC API直接调用来实现对数据库的访问。本系统的构造模型是以
    数据库为载体的构模形式,系统机构图如1所示。图1
    图1中知识获取结构负责建立、修改与扩充各个数据库;解释机构
    用于对求解过程作
    出说明,指出求解成功或失败的原因,并回答用户提出的问题。事
    实库用来存放输入的原始事实及中间结果;字典库用来存放规则中事
    实的基本定义和说明;规则库用来存放规则;垃圾桶用来存放推理中失
    败的推理路径。
    二、规则与数据库的设计
    1. 产生式规则的模型
    规则的一般形式是:
    if〈前提〉then〈结论〉
    它表示当〈前提〉成立时,得出〈结论〉的可信度为。其中〈前
    提〉是事实或断言的合取形式。本系统中的规则模型请参考图2。
    2. 事实库
    结构:FACT_DB(Fact_ID,Rank,No)
    用途:存放输入的原始事实,中间结果及最后结果。
    其中:Fact_ID是事实Fact的编码;Rank用来表示系统特定部分,比
    如说"放大级"、"槽路"等;No表示特定部分中的部件的编号;如"1"表
    示"槽路"部分1号管、"2"表示"槽路"部分2号管等。
    3. 字典库
    结构:DICT_DB(Fac_ID,Component,Appear,Why,Known)
    用途:存放规则库中的前提条件和结论及其编码。
    其中:Fact_ID为事实编码;Component为部件名称;Appear是对Fac
    t_ID的自然语言解释;Known用来表示该事实已知或未知,以防止该断
    言的重复求证。
    4. 规则库
    规则库中包括四个表(TABLE),它们是规则前件库(PRE_TABLE)、
    已激活的规则前件库(ACTIVE_PRE TABLE)、规则后件库(ACT_TABLE)
    和已激活的规则后件库(ACTIVE_ACT_TAB LE)。
    图2
    (1)规则前件库
    结构:PRE_TABLE (Rule_Name, Fact_ID)
    用途:存放各条规则对应的前提条件。
    其中:Rule_Name为规则名;Fact_ID为Rule_Name规则的一个与条
    件;一条规则的n个与条件在该库中就有n条对应该规则的记录。
    (2)已激活的规则前件库
    结构:ACTIVE_PRE_TABLE(Fact_ID,Rank,No)
    用途:存放已激活的前提条件,以避免规则各前提条件的重复匹配

    其中:Fact_ID为Rule_Name规则的一个与条件;Rank用来表示系统
    特定部分;No表示特定部分中的部件的编号。
    (3)规则后件库
    结构:ACT_TABLE (Rule_Name, Fact_ID, Num, Num2)
    用途:存放规则对应的结果。
    其中:Rule_Name为规则名;Fact_ID为Rule_Name规则的结果;Num
    表示该规则前提条件的个数;Num2为Num字段的辅助值。
    (4)已激活的规则后件库
    结构:ACTIVE_ACT_TABLE(Rule_Name, Rank,No)
    用途:存放已激活的后件,以避免规则各结论的重复匹配。
    其中:Rule_Name为规则名;Rank用来表示系统特定部分;No表示特
    定部分中的部件的编号。
    5. 垃圾桶
    结构:GARBAGE_BIN_DB(Fact_ID, Rule_Name, Pre_Num)
    用途:记录剪去枯死枝叶的原因。
    其中:Fact_ID为事实编码;Rule_Name为应用于该结点的规则名,P
    re_Num为实际匹配的前提条件数。
    三、控制机构的设计
    我们用C++语言实现了一个采用深度优先策略的反向推理机。整
    个推理过程。就是一棵搜索树边长枝边修枝的过程推理机的源程序如
    下:
    int CCause::Reason(RTree*rTree)
    {
    RULE prule,rule;
  • 2005-09-01

    校验框类问题 - [VC专栏]

    问:我想创建这样一个校验框类,它的自动特性只出现在用户点击校验标记时,
    而不是当文本 被点击时.在点击文本时,我只想设置焦点,就象CheckListBox那样.
    答:在按钮类的派生类中,例如CCheckBox,处理WM_LBUTTONDOWN消息:

    void CCheckBox::OnLButtonDown( UINT nFlags, Cpoint point )
    {
    // if the checkmark was clicked, pass the message onto the button
    if ( HitTest( point ) )
    Cbutton::OnLButtonDown( nFlags, point );
    else // otherwise set the focus only
    SetFocus();
    }

    BOOL CCheckBox::HitTest( Cpoint& point )
    {
    Crect rcClient;
    GetClientRect( rcClient );

    // Checkmark rect
    Crect rc;
    // Checkmark square size, hard coded here because I couldn’t find any method to obtain this value from
    the system.
    // Maybe someone else knows how to do it?
    Const int nCheckSize = 13;

    DWORD dwStyle = GetStyle();
    if ( ( dwStyle & BS_VCENTER ) == BS_VCENTER )
    rc.top = rcClient.top + ( ( rcClient.bottom - rcClient.top ) -nCheckSize ) / 2;
    else if ( dwStyle & BS_TOP )
    rc.top = rcClient.top + 1;
    else if ( dwStyle & BS_BOTTOM )
    rc.top = rcClient.bottom - nCheckSize - 2;
    else // Default
    rc.top = rcClient.top + ( ( rcClient.bottom - rcClient.top ) -nCheckSize ) / 2;

    if ( dwStyle & BS_LEFTTEXT )
    rc.left = rcClient.right - nCheckSize;
    else
    rc.left = rcClient.left;

    rc.right = rc.left + nCheckSize;
    rc.bottom = rc.top + nCheckSize;
    return rc.PtInRect( point );
    }
  • 问:我编了一个小巧而有趣的工具,当用户使用时我不想让它显示出任何用户
    界面。听听各位有办法可将视关闭。


    答:你可以注册一个新的窗口类型,它拥有除了WS_VISBLE属性外的任何属性,
    类似CFrameWnd,在PreCreateWindow方法中实现。
    另外,你能在OnCreate方法中通过设置m_nCmdShow为SW_HIDE来实现,
    具体方法如下:

    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
    return -1;

    // hide our app
    AfxGetApp()->m_nCmdShow = SW_HIDE;

    return 0;
    }
  • andymei 收藏

    摘 要: 四通利方(RichWin),中文之星(CStar)是大家广为熟知的汉化Windows产
    品,“陷阱”技术即动态修改Windows代码,一直是其对外宣称的过人技术,它
    究竟是如何实现的,这自然是核心机密。本文试图解开这个秘密,并同时介
    入Windows的模块调用机制与重定位概念,并给出了采用"陷阱"技术动态修改
    Windows代码的示例源程
    序。

    一、发现了什么?
    作者多年来一直从事Windows下的软件开发工作,经历 了Windows2.0、3.0、
    3.1,直至WindowsNT,95的成长过程,也遍历了长青窗口、长城窗口、DBWin、
    CStar、RichWin等多个Windows汉化产品。从现在看来,影响最大也最为成功的,
    当推四通利方的RichWin,此外,中文之星CStar与RichWin师出一门,其核心技
    术自然也差不许多。其对外宣传采用独特的“陷阱”技术动态修改Windows代码,
    一直是作者感兴趣的地方。

    EXEHDR是MicrosoftVisualC++开发工具中很有用的一个程序,它可以检查
    NE(New_Executable)格式文件,用它来分析RichWin的WSENGINE.DLL或CStar
    的CHINESE.DLL就会发现与众不同的两点:
    ( 以CStar 1.20 为 例)
    C:/CSTAR>exehdr chinese.dll /v
    ..................................
    6 type offset target
    BASE060aseg 2 offset 0000
    PTR 047eimp GDI.GETCHARABCWIDTHS
    PTR 059bimp GDI.ENUMFONTFAMILIES
    PTR 0451imp DISPLAY.14( EXTTEXTOUT )
    PTR 0415imp KEYBOARD.4( TOASCII )
    PTR 04baimp KEYBOARD.5( ANSITOOEM )
    PTR 04c9imp KEYBOARD.6( OEMTOANSI )
    PTR 04d8imp KEYBOARD.134( ANSITOOEMBUFF)
    PTR 05f5imp USER.430( LSTRCMP )
    PTR 04e7imp KEYBOARD.135( OEMTOANSIBUFF)
    PTR 0514imp USER.431( ANSIUPPER)
    PTR 0523imp USER.432( ANSILOWER )
    PTR 05aaimp GDI.56( CREATEFONT)
    PTR 056eimp USER.433( ISCHARALPHA )
    PTR 05b9imp GDI.57( CREATEFONTINDIRECT )
    PTR 057dimp USER.434( ISCHARALPHANUMERIC )
    PTR 049cimp USER.179( GETSYSTEMMETRICS )
    PTR 0550imp USER.435( ISCHARUPPER)
    PTR 055fimp USER.436( ISCHARLOWER)
    PTR 0532imp USER.437( ANSIUPPERBUFF)
    PTR 0541imp USER.438( ANSILOWERBUFF)
    PTR 05c8imp GDI.69( DELETEOBJECT )
    PTR 058cimp GDI.70( ENUMFONTS )
    PTR 04abimp KERNEL.ISDBCSLEADBYTE
    PTR 05d7imp GDI.82( GETOBJECT)
    PTR 048dimp KERNEL.74 ( OPENFILE )
    PTR 0460imp GDI.91( GETTEXTEXTENT)
    PTR 05e6imp GDI.92( GETTEXTFACE)
    PTR 046fimp GDI.350 ( GETCHARWIDTH )
    PTR 0442imp GDI.351 ( EXTTEXTOUT )
    PTR 0604imp USER.471( LSTRCMPI )
    PTR 04f6imp USER.472( ANSINEXT )
    PTR 0505imp USER.473( ANSIPREV )
    PTR 0424imp USER.108( GETMESSAGE )
    PTR 0433imp USER.109( PEEKMESSAGE)
    35 relocations

    *******扩号内为作者加上的对应WindowsAPI函数
    第一,在数据段中,发现了重定位信息。
    第二,这些重定位信息提示的函数,全都与文字显示
    输出和键盘,字符串有关。也就是说汉化Windows,必须修改这些函数。
    在这非常特殊的地方,隐藏着什么呢?无庸致疑,这与众不同的两点,对打开
    “陷阱”技术之门而言,不是金钥匙,也是敲门砖。

    二、Windows的模块调用机制与重定位概念
    为了深入探究“陷阱”技术,我们先来介绍Windows的模块调用机制。Windows的
    运行分实模式(RealMode),标准模式(StandMode)和增强模式
    (386EnhancedMode)三种,虽然这几种模式各不相同,但其核心模块的调用
    关系却是完全一致的。
    主要的三个模块,有如下的关系:

    KERNEL是Windows系统内核,它不依赖其它模块。

    GDI是Windows图形设备接口模块,它依赖于KERNEL模块。

    USER是Windows用户接口服务模块,它依赖于KERNEL,GDI模块及设备驱动程序
    等所有模块。
    这三个模块,实际上就是Windows的三个动态连接库,在系统的存在形式如下,
    KERNEL有三种不同形式,Kernel.exe(实模式),Krnl286.exe(标准模式),Krnl386.
    exe(386增强模式);GDI模块是Gdi.exe;USER模块是User.exe,虽然文件名都
    以EXE为扩展名,但它们实际都是动态连接库。

    同时,几乎所有的API函数都隐藏在这三个模块中。用EXEHDR对这三个模块分
    析,就可列出一大堆你所熟悉的WindowsAPI函数。

    以GDI模块为例,

    C:/WINDOWS/SYSTEM>exehdr gdi.exe
    Exports:
    ord seg offset name
    ............
    351 1923eEXTTEXTOUT exported, shared data
    56 319e1CREATEFONT exported, shared data
    ............

    至此,你已能从Windows纷繁复杂的系统中,理出一些头续来。下面,再引入一
    个重要概念——重定位。
    一个Windows执行程序对调用API函数,或对其它动态库的调用,在程序装入内
    存前,都是一些不能定位的动态连接,当程序调入内存时,这些远调用都需要
    重新定位,重新定位的依据就是重定位表。在Windows执行程序(包括动态库)
    的每个段后面,通常都跟有这
  • 作者: Oleg Galkin.

    MFC静态分割窗口有很多局限性。它们不能动态的显示或隐藏窗格。为了解决这
    个问题,我重载了CSplitterWnd。新的代码将支持这一功能。


    ///
    /
    // splitex.h
    // (c) 1997, Oleg G. Galkin

    class CSplitterWndEx : public CSplitterWnd
    {
    protected:
    int m_nHidedCol; // hided column number, -1 if all columns
    // are shown

    public:
    CSplitterWndEx();

    void ShowColumn();
    void HideColumn(int colHide);

    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CSplitterWndEx)
    //}}AFX_VIRTUAL

    // Generated message map functions
    protected:
    //{{AFX_MSG(CSplitterWndEx)
    // NOTE - the ClassWizard will add and remove
    //member functions here.
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
    };

    /
    /
    // splitex.cpp
    // (c) 1997, Oleg G. Galkin

    #include "stdafx.h"
    #include "splitex.h"

    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif

    ///
    /
    // CSplitterWndEx

    CSplitterWndEx::CSplitterWndEx() :
    m_nHidedCol(-1)
    {
    }

    void CSplitterWndEx::ShowColumn()
    {
    ASSERT_VALID(this);
    ASSERT(m_nCols < m_nMaxCols);
    ASSERT(m_nHidedCol != -1);

    int colNew = m_nHidedCol;
    m_nHidedCol = -1;
    int cxNew = m_pColInfo[m_nCols].nCurSize;
    m_nCols++; // add a column
    ASSERT(m_nCols == m_nMaxCols);

    // fill the hided column
    int col;
    for (int row = 0; row < m_nRows; row++)
    {
    CWnd* pPaneShow = GetDlgItem(
    AFX_IDW_PANE_FIRST + row * 16 + m_nCols);
    ASSERT(pPaneShow != NULL);
    pPaneShow->ShowWindow(SW_SHOWNA);

    for (col = m_nCols - 2; col >= colNew; col--)
    {
    CWnd* pPane = GetPane(row, col);
    ASSERT(pPane != NULL);
    pPane->SetDlgCtrlID(IdFromRowCol(row, col + 1));
    }

    pPaneShow->SetDlgCtrlID(IdFromRowCol(row, colNew));
    }

    // new panes have been created -- recalculate layout
    for (col = colNew + 1; col < m_nCols; col++)
    m_pColInfo[col].nIdealSize =
    m_pColInfo[col - 1].nCurSize;
    m_pColInfo[colNew].nIdealSize = cxNew;
    RecalcLayout();
    }

    void CSplitterWndEx::HideColumn(int colHide)
    {
    ASSERT_VALID(this);
    ASSERT(m_nCols > 1);
    ASSERT(colHide < m_nCols);
    ASSERT(m_nHidedCol == -1);
    m_nHidedCol = colHide;

    // if the column has an active window -- change it
    int rowActive, colActive;
    if (GetActivePane(&rowActive, &colActive) != NULL &&
    colActive == colHide)
    {
    if (++colActive >= m_nCols)
    colActive = 0;
    SetActivePane(rowActive, colActive);
    }

    // hide all column panes
    for (int row = 0; row < m_nRows; row++)
    {
    CWnd* pPaneHide = GetPane(row, colHide);
    ASSERT(pPaneHide != NULL);
    pPaneHide->ShowWindow(SW_HIDE);
    pPaneHide->SetDlgCtrlID(AFX_IDW_PANE_FIRST + row * 16 + m_nCols);

    for (int col = colHide + 1; col < m_nCols; col++)
    {
    CWnd* pPane = GetPane(row, col);
    ASSERT(pPane != NULL);
    pPane->SetDlgCtrlID(IdFromRowCol(row, col - 1));
    }
    }
    m_nCols--;
    m_pColInfo[m_nCols].nCurSize = m_pColInfo[colHide].nCurSize;
    RecalcLayout();
    }

    BEGIN_MESSAGE_MAP(CSplitterWndEx, CSplitterWnd)
    //{{AFX_MSG_MAP(CSplitterWndEx)
    // NOTE - the ClassWizard will add and remove mapping macros here.
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
  • 2005-09-01

    显示工具条 - [VC专栏]

    问:在我的程序中想在有些窗口打开或关闭时显示,隐藏某些工具条,这些我也都
    做到了,但 我一直不能将那些工具条放置到我想放的位置,我的主工具条非常小,
    它的右边可以放些工 具条(这就是我所想的)但它们总是显示在主工具条的下方.

    答:我从一个示例文件中找到我现在的代码(抱歉我已经记不清从哪儿得来了).

    I use the following code which I snitched from one of the examples (sorry,

    don’t remember where).



    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

    {

    ......

    // create scalebar toolbar as generated by MFC wizard

    ....

    DockControlBarLeftOf(&m_wndScaleBar,&m_wndToolBar );

    }



    void CMainFrame::DockControlBarLeftOf(CToolBar* Bar,CToolBar* LeftOf)

    {

    Crect rect;

    DWORD dw;

    UINT n;



    // get MFC to adjust the dimensions of all docked ToolBars

    // so that GetWindowRect will be accurate

    RecalcLayout();

    LeftOf->GetWindowRect(&rect);

    rect.OffsetRect(1,0);

    dw=LeftOf->GetBarStyle();

    n = 0;

    n = (dw&CBRS_ALIGN_TOP) ? AFX_IDW_DOCKBAR_TOP : n;

    n = (dw&CBRS_ALIGN_BOTTOM && n==0) ? AFX_IDW_DOCKBAR_BOTTOM : n;

    n = (dw&CBRS_ALIGN_LEFT && n==0) ? AFX_IDW_DOCKBAR_LEFT : n;

    n = (dw&CBRS_ALIGN_RIGHT && n==0) ? AFX_IDW_DOCKBAR_RIGHT : n;



    // When we take the default parameters on rect, DockControlBar will dock

    // each Toolbar on a separate line. By calculating a rectangle, we in effect

    // are simulating a Toolbar being dragged to that location and docked.

    DockControlBar(Bar,n,&rect);

    }
  • 显示HTML模式的对话框类


    作者:V. Rama Krishna 译:杨勇

    我总是想在我的应用程序“关于”对话框中加入更多的图像,动画与音乐。但
    它需要许多额外的工作,我没有胆量冒风险去做这些。幸运的是,
    Internet Explorer 4.0的到来,在新的技术介绍中我们能做所有的这些而不
    需要写大量的代码。一个办法是用web browser控件,但不总是最好的解决方
    法,得别是对于模式对话框。这里另外的技术用在模式对话框-一种使用IE4的
    技术引进。 模式对话框仅挂钩mshtml.dll(HTML描述与分析的动力)。因此许
    多重要的功能如能够点击一链接和在同样的窗口中显示链接,但收藏夹与历史管
    理是不起作用的(see Reusing the WebBrowser and MSHTML in Internet Client SDK)。
    这是足够好的,几乎可放置在我们仅需要模式对话框的任何地方。

    如何使用HTML模式的对话框

    函数SHOWHTMLDIALOGFN在mshtml.dll库中,可以显示HTML对话框,但它通常需
    要COM stuff 。为简单的做到这些我开发了CHtmlDialog类。所有的COM stuff
    被隐藏,这个类能容易的使用到几乎所有的应用程序中。

    一个简单的使用CHtmlDialog类的案例。

    在这个案例中你需要将它用作为一个“关于”对话框。所有的你必须做的是写
    一个HTML文件,并加入到资源文件中其ID为IDR_ABOUT_HTM。修改你的
    OnAppAbout函数如下: 

    void CHDDemoApp::OnAppAbout()

    {

    CHtmlDialog dlg(IDR_ABOUT_HTM, AfxGetMainWnd());

    dlg.DoModal();

    }

    这个构造函数也允许你使用字符串或URLs地址。详细的请看示例工程。正是通过
    这个代码你能够 使用图像与声音。T

    使用参数到对话框。

    有时侯我们可能需要显示参数。在我们的应用程序中加入下列代码: 

    void CHDDemoApp::OnDemoParam1()

    {

    CHtmlDialog dlg(IDR_ABOUT1_HTM, AfxGetMainWnd());

    CString str = m_strProductID //product ID

    + ";"+ m_strUserName //User Licensed + ";"

    + m_strCompanyName //Company Name

    + ";" + m_strAppVersion;

    dlg.SetParam(str);

    dlg.DoModal();

    }

    在HTML中我们有时要加入一些javascript/vbscript函数,如果你不熟悉
    IE4 DHTM你可以跳过。下面是一个SCRIPT函数。  

    function getParameters()

    {

    var args = new Array();

    args = window.dialogArguments.split(";");

     

    //Now display in the document

    Productid.innerText = args[0];

    UserName.innerText = args[1];

    CompanyName.innerText = args[2];

    AppVersion.innerText = args[3];

    }

    从对话框中得到返回值:

    现在我们可以使用Javascript和C++,我们能够做得更多。如何通过参数使使用
    者在对话框中所做的事作为结果返回到C++程序中呢?下面这个示例,让我们
    修改“用户名”和“公司名”,要做到这一点,我们必做到下面的关于
    Javascript和C++的描述。

    当HTML窗口被关闭时将调用 

    function window_onclose()

    {

    window.returnValue = UserName.value + ";" + CompanyName.value;

    }

    通过设定返回值,这个值被转移给C++代码。这个返回值可以是任何变量。在
    我们的C++代码是通过调用GetReturnString或GetReturnVariant得到的。 

    dlg.DoModal(); //显示对话框

    CString str = dlg.GetReturnString();

    设置对话框尺寸

    <HTML style="width: 25em; height: 30em>

    注释:

    运行环境:

    1. Internet Explorer 4.0 (or greater). (IE4.0 SP1 is recommended)

    2. VC++ 6.0 (or greater) or VC++ 5.0 (with Internet Client SDK)
  • 2005-09-01

    位图 - [VC专栏]

    问:对一个图形对象,我想用一种颜色来替换原位图调色板中的某一颜色。另
    外不管 系统是处于16位,32位,256色是不是都可以用上面的方法解决。
    答:我认为你必须对每种情况作出不同的处理,256色可以通过调色板来改
    变某一颜色,但在 真彩系统中必须重绘图中的相应的点才可以。(位图是
    真彩色的)

    #define DSPDxax 0x00E20746

    void CSJSizeBar::SwapBitmapColor(CDC* pDC, Cbitmap* pBitmap,
    COLORREF rgbNew, COLORREF rgbOld)
    {
    BITMAP bm;
    Cbitmap bmMask;
    CDC memDC,maskDC;

    // create memory dc for drawing
    memDC.CreateCompatibleDC( pDC );
    Cpalette* pOldPalette = (Cpalette*)memDC.SelectPalette(
    pDC->GetCurrentPalette(),FALSE );
    memDC.RealizePalette();
    Cbitmap* Bitmap = memDC.SelectObject( pBitmap );

    // fill out bitmap structure
    pBitmap->GetBitmap( &bm );

    // create mask
    bmMask.CreateBitmap( bm.bmWidth,bm.bmHeight,1,1,NULL );

    maskDC.CreateCompatibleDC( &memDC );
    Cbitmap* bmOld = maskDC.SelectObject( &bmMask );

    Cbrush brush( rgbNew );
    Cbrush* brOld = (Cbrush*)memDC.SelectObject( &brush );

    memDC.SetBkColor( rgbOld );
    maskDC.BitBlt( 0,0,bm.bmWidth,bm.bmHeight,&memDC,0,0,SRCCOPY );

    memDC.SetBkColor( RGB(255,255,255) );
    memDC.SetTextColor( RGB(0,0,0) );
    memDC.BitBlt( 0,0,bm.bmWidth,bm.bmHeight,&maskDC,0,0,DSPDxax );

    maskDC.SelectObject( bmOld );
    memDC.SelectObject( &brOld );
    memDC.SelectObject( Bitmap );
    memDC.SelectPalette( pOldPalette,FALSE );
    }
  • 问:我在MDI程序中增加了一个CRichEditView文档模板,在子窗口视中我增加了下
    面一些代码.

    StartReport (void)
    {
    CReportFrame *rpt;
    CReportDoc *rptDoc;

    // First get the right document template
    POSITION pPos = theApp.GetFirstDocTemplatePosition();
    theApp.GetNextDocTemplate ( pPos );
    theApp.GetNextDocTemplate ( pPos );
    CDocTemplate *pTemplate = theApp.GetNextDocTemplate ( pPos );

    // Verify validity
    ASSERT(pTemplate != NULL);
    ASSERT_KINDOF(CDocTemplate, pTemplate);

    // Create the frame
    rptDoc = new CReportDoc;
    rpt = (CReportFrame*)pTemplate->CreateNewFrame ( rptDoc, NULL );
    pTemplate->InitialUpdateFrame (rpt, rptDoc);

    // Get access to the display area
    CReportView *rptView = static_cast
    (rpt->GetActiveView());
    CRichEditCtrl &rptCtrl = rptView->GetRichEditCtrl();
    }
    CReportFrame继承于CMDIChildWnd
    CReportDoc继承于CRichEditDoc
    CReportView继承于from CRichEditView
    如果我关闭程序前不关闭新建的视,调试器将认为程序依然在运行(程序管理器
    中依然存在) 我需要用调试菜单中的stop debugging来关闭程序;如果我手工关
    闭该视,程序将会正常 关闭.如果有什么不同的话,在手工关闭新的视之前程序会
    询问是否保存. 那么怎样我才能关闭程序呢?


    答:1)我也碰上过对话框,窗口不能自动关闭的情况,这主要是因为继承的对象不正
    确所造成的。 通常应该在主程序中设置AfxGetMainWnd().
    你的程序让我搞糊涂了,一连使用了多个GetNextDocTemplate(pPos),在这些
    文档指针是NULL时 通常会引起一些循环.在你的文档模板中是否已经精心算好
    了数目?这样可能会产生些bugs 我建议找出当前的文档模板用
    CDocTemplate::CreateNewDocument()来代替你的"new CReportDoc"
    2) 记住一个公共规则,关闭程序前要关闭所有的视.
  • 作者:俞良军


    在MFC中,用列表框(CListBox)来显示多个字符串是一种很方便的方法。但缺
    省的列表框水平滚动条不够智能——这里智能的含义是:在应该出现的时候出
    现,不应该出现的时候消失,而且应能自动调节自己的大小。本文通过实例说
    明了存在的问题和解决办法。

    ---- 一、问题演示

    ---- 首先用Visual Studio应用向导创建工程CustomCListBox。这是一个基于对
    话框的应用,向导提供的所有可选参数均采用其缺省值。

    ---- 在资源编辑器中将对主话框字体设为宋体12,插入一个CListBox控制,设
    其ID为IDC_LLISTTEST,大小为125 X 84。 请确认列表框的垂直滚动条、水平滚
    动条有效,取消其排序风格。

    ---- 启动Class Wizard,选择Member Variables选项卡,为列表框加入对应的
    成员变量m_lListTest,在Category中选择Control。

    ---- 接下来在Workspace窗格中选择ClassView,扩展CCustomCListBoxDlg类并
    双击OnInitDialog(),在编辑窗格中找到注释行“TODO: Add extra initialization here”,
    在该行下面加入以下内容:

    m_lListTest.AddString(_T("One"));
    m_lListTest.AddString(_T("Two"));
    m_lListTest.AddString(_T("Three"));
    m_lListTest.AddString(_T("Four"));
    m_lListTest.AddString(_T("Five"));
    m_lListTest.AddString(_T("Six"));
    m_lListTest.AddString(_T("北国风光,千里冰封,万里雪飘。"));
    m_lListTest.AddString(_T("Eight"));
    m_lListTest.AddString(_T("Nine"));
    m_lListTest.AddString(_T("Ten"));


    ---- 编译并运行这个工程,可以发现列表框能够正确显示全部内容。

    ---- 如果在上述m_lListText.AddString(_T"Ten"))后面加入一行:

    m_lListTest.AddString(_T("Eleven"));

    ---- 重新编译并运行该工程,可以发现出现了一个垂直滚动条。垂直滚动条的出
    现使得列表框水平方向有效显示宽度变小,第七行的内容被切割而不能完整显
    示。但此时水平滚动条并没有自动出现,第七行被切割部分就无法看到了。

    ---- 如果我们删除最后加入的语句,把第七行汉字加长到超出列表框显示宽度
    为止,也可以发现水平滚动条不会自动出现。被切割部分仍旧无法看到。

    ---- 由此可知,CListBox的水平滚动条并不象垂直滚动条那样“聪明”:垂直
    滚动条总是能够在需要它的时候自动出现,并能够自动调节自身大小,而水平
    滚动条不能。

    ---- 二、解决问题

    ---- 为提高代码的可重用性,可以创建CListBox的派生类,在派生类中实现
    “智能”水平滚动条。需要考虑的主要问题包括:跟踪最大字符串宽度(应能适
    应不同场合下的字体变化),必要时计算垂直滚动条宽度,自动显示和调节水平
    滚动条的大小。

    ---- 选菜单 Insert/New Class,设新创建类的名字为CDJListBox,其基类为
    CListBox,其它选项采用缺省值。单击OK,Visual Studio自动生成
    DJListBox.cpp和DJListBox.h两个文件。

    ---- 接下来将主对话框的列表框改为CDJListBox类型,即在CLassView扩展
    CCustomListBoxDlg类并双击m_lListTest成员,在编辑窗格,修改

    CListBox m_lListTest;


    ---- 为:

    CDJListBox m_lListTest;


    ---- 然后,在类声明代码之前,插入

    #include "DJListBox.h"


    ---- 此时如果重新编译并运行,是无法看到任何实质性的改变的,因为我们并
    没有修改CDJListBox。所有对于CDJListBox的调用都直接传递给基类CListBox了。

    ---- 跟踪字符串最大宽度可以通过覆盖CListBox::AddString()实现。打开DJListBox.h,
    紧接类的析构函数加入如下声明:

    int AddString( LPCTSTR lpszItem );


    ---- 并在实现文件DJListBox.cpp加入该函数框架:

    int CDJListBox::AddString(LPCTSTR lpszItem)
    {
    //此处加入字符串宽度跟踪、水平滚动条显示等代码
    }


    ---- 字符串宽度跟踪可以用整形成员变量m_nMaxWidth实现。在DjListBox.h的
    protected声明区内,加入以下一行:

    int m_nMaxWidth;


    ---- 在DJListBox.cpp文件,找到CDJListBox的建构函数,为这个最大宽度作
    初始化:

    m_nMaxWidth = 0;


    ---- 现在可以改动新加入的AddString()了。先应该调用基类AddString(),
    并用nRet记录其返回值:

    int nRet = CListBox::AddString(lpszItem);


    ---- 接下来调用GetScrollInfo()以获得垂直滚动条的相关信息。这些信息是
    通过一个SCROLLINFO结构传递的,下面是对该结构初始化并调用GetScrollInfo()的代码:

    SCROLLINFO scrollInfo;
    memset(&scrollInfo, 0, sizeof(SCROLLINFO));
    scrollInfo.cbSize = sizeof(SCROLLINFO);
    scrollInfo.fMask = SIF_ALL;
    GetScrollInfo(SB_VERT, &scrollInfo, SIF_ALL);


    ---- 在调试器内观察SCROLLINFO,可以发现要获得nMax和nPage的正确数值,
    列表框至少应含有一个字
  • 在你的对话框中加入下代码,可实现拖动对话框的任何地方拖动对话框。
    void CNCHitDlg::OnLButtonDown(UINT nFlags, CPoint point)
    {
    CDialog::OnLButtonDown(nFlags, point);
    // fake windows into thinking your clicking on the caption, does not
    // maximize on double click
    PostMessage( WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM( point.x, point.y));
    }

    UINT CNCHitDlg::OnNcHitTest(CPoint point)
    {
    UINT nHitTest = CDialog::OnNcHitTest( point );
    // also fake windows out, but this maximizes
    // the window when you double
    // click on it.
    return (nHitTest == HTCLIENT) ? HTCAPTION : nHitTest;
    }
  • 问:如果我在文件对话框中选择了某个文件,然后点击"打开"按钮,DoModal()函数
    返回 1(IDOK),但如果我选择了多个文件,DoModal()函数却返回2(IDCANCEL)

    答:如果设置了OFN_ALLOWMULTISELECT标志,而又选择了多个文件时.指针指向一
    个可以容纳 所选择文件名的区域.这个缓冲区包括了所选择的文件的文件名以
    及路径.在Explorer 类型的对话框中,路径和文件名字符串是用NULL分隔的,在
    最后一个文件名中以NULL结尾. 老式的对话框中,串是用空格来分隔的.你可以
    使用FindFirstFile函数来转换长文件名 和短文件名,如果这个缓冲区太小的
    话,函数将会返回FALSE,CommDlgExtendedError() 函数返回
    FNERR_BUFFERTOOSMALL,在这种情况下,最前两个字节指出缓冲区应该的大小,
    单位是字节或者是字符数.
    nMaxFile
    指定缓冲区的大小,以一个字节为单位(ANSI或UNICODE),如果该区域太小不能容
    纳 文件信息时,GetOpenFileName和GetSaveFileName函数返回FALSE.该区域至少
    为 256字符数
    简单地说,你需要提供比文件对话框提供的空间一个更大的空间,前者只有
    _MAX_PATH长.
    2)你是否调用了CommDlgExtendError取得更详细的资料?
    我想可能是给OPENFILENAME结构的缓冲区太小了,MFC中只提供了
    _MAX_PATH或260个字符 而这对选择多个文件来说,显得小了些.
    你可以调用CommDlgExtendedError函数,应该返回一个FNERR_BUFFERTOOSMALL,
    参照 文件说明中的OPENFILENAME结构.
  • ---- 在使用VC++5.0开发应用程序时,我们可能需要改变框架窗口(包括主框架窗口和
    子框架窗口)的图标,而不是使用VC为你已经准备好了的、很难看的那个图标。但笔者
    参考了很多书籍、资料,却没有找到任何叙述如何去改变窗口图标的方法的文章(或许
    很多人认为这根本就不值得一提),让很多初学者无从着手。笔者经过实践,发现了以
    下两个改变框架窗口图标的方法。使用方法一可以在编写应用程序时指定框架窗口的图
    标,使用方法二可以在程序运行时根据需要动态地改变窗口的图标。如果把这两个方法
    结合起来,就可以随心所欲改变窗口的图标。

    ---- 方法一、在编程时指定窗口的图标

    ---- 一、如果是指定主框架窗口的图标,其步骤如下:

    创建或打开工程Icon(以下都以工程名为Icon为例)。

    单击Workspace窗口的ResourceView标签,选中资源ID为IDR_MAINFRAME图标资源,然后
    按Delete键把它删除掉。注意:一定要把它删除才行。

    从Developer Studio的Insert菜单中选择Resource,然后选择Icon,新建(New)一个
    新的图标或导入(Import)一个已有的图标。

    把新图标的资源ID改为AFX_IDI_STD_MDIFRAME(如果是MDI应用程序)或改为
    AFX_IDI_STD_FRAME(如果是SDI应用程序)。AFX_IDI_STD_MDIFRAME和
    AFX_IDI_STD_FRAME这两个资源ID是MFC中预定义了的。

    编译并运行程序,可以发现主框架窗口的图标就是你指定的图标。
    ---- 二、如果是指定MDI子框架窗口的图标,其步骤与上述相似。

    同上。创建或打开工程Icon。

    删除资源ID为IDR_ICONTYPE(在你的工程中应该是IDR_XXXTYPE,其中XXX为你的工程
    名)图标资源。同样要注意的是:一定要把它删除才行。

    同上。新建(New)一个新的图标或导入(Import)一个已有的图标。

    把新图标的资源ID改为IDR_ICONTYPE(即步骤2中删除的资源ID)。

    编译并运行程序,可以发现子框架窗口的图标就是你指定的图标。
    ---- 用这个方法,可以在多视图类MDI应用程序中为不同视图的子框架窗口指定不同的
    图标。

    ---- 方法二、在程序运行时动态地改变窗口的图标

    ---- 在程序运行时动态地改变框架窗口图标的原理是使用函数

    ---- CWnd::SendMessage()向窗口发送WM_SETICON消息。其方法是:

    HICON hIcon=AfxGetApp()- >LoadIcon(IDI_ICON1);
    ASSERT(hIcon);
    AfxGetMainWnd()- >SendMessage(WM_SETICON,TRUE,(LPARAM)hIcon);

    ---- 以上叙述中的AfxGetMainWnd()是获得主框架窗口的窗口句柄,所以改变的是主框
    架窗口(包括MDI和SDI)的图标,用同样的方法略作改动就可以改变MDI应用程序子框
    架窗口的图标。

    ---- 下面举一个实例来说明如何改变主框架窗口的图标,步骤如下:

    ---- 1.创建或打开工程Icon。

    ---- 2. 从Developer Studio的Insert菜单中选择Ressource,然后选择Icon,新建
    (New)或导入(Import)两个图标,并资源ID分别改为IDI_ICON_GREEN,
    IDI_ICON_RED。

    ---- 3.单击Workspace窗口的ResourceView标签,对IDR_MAINFRAME菜单资源进行编
    辑。在View菜单中加入一个分隔符和Green Icon、Red Icon两个菜单项。其资源ID分别
    改为ID_VIEW_GREEN和ID_VIEW_RED。

    ---- 4.为主窗口添加如下消息处理函数:

    // CMainFrame message handlers
    void CMainFrame::OnViewGreen()
    {
    // TODO: Add your command handler code here
    HICON hIcon=AfxGetApp()- >LoadIcon(IDI_ICON_GREEN);
    ASSERT(hIcon);
    SendMessage(WM_SETICON,TRUE,(LPARAM)hIcon);
    file://因为是在类CmainFrame中,所以不需要用
    AfxGetMainWnd()- >SendMessage(WM_SETICON,TRUE,(LPARAM)hIcon);
    }

    void CMainFrame::OnViewRed()
    {
    // TODO: Add your command handler code here
    HICON hIcon=AfxGetApp()- >LoadIcon(IDI_ICON_RED);
    ASSERT(hIcon);
    SendMessage(WM_SETICON,TRUE,(LPARAM)hIcon);
    }

    ---- 5. 最后编译并执行程序,执行View菜单的Green Icon和Red Icon,可以看到成功
    得改变主框架窗口的图标。

    ---- 以上实例是在程序运行时改变主框架窗口的图标,可以用同样的方法改变MDI程序
    的子框架窗口的图标,有兴趣的朋友可以一试。
  • 在使用VC6.0/5.0的AppWizard生成MDI应用的时候,我们发现MDI主窗口的客
    户区背景千篇一律的是深灰的。VC6.0/5.0并没有提供修改其背景色的方法。甚
    至使用SDK编程也没有好的方法修改背景色。以至于微软的产品如Office也是灰蒙
    蒙的背景。那么,有没有办法将背景设置为自己喜欢的颜色呢?

    笔者在学习过程中摸索出一套随意改变客户区窗口颜色的方法。利用这套方法,
    可以将客户区窗口设为256色背景甚至设为BITMAP位图以至于动画等等。大大地增
    强了程序的多媒体效果。



    先介绍对MDI客户窗口编程的基本原理。

    一、MDI客户窗口

    一个MDI应用的主框架窗口包含一个特殊的子窗口称为MDICLIENT窗口。
    MDICLIENT窗口负责管理主框架窗口的客户区。MDICLIENT窗口本身有自己的
    子窗口即由CMDIChildWnd派生的文档窗口,也就是MDI子窗口。MDI主框架窗口
    负责管理MDICLIENT子窗口。当控制条(菜单条,状态条等)发生变化时,MDI主
    框架窗口重新配置MDICLIENT窗口。MDICLIENT子窗口负责管理全部的MDI子窗口。
    父窗口负责将某些命令传递到子窗口。因此,消息队列发向MDI子窗口的消息
    由MDICLIENT窗口负责传递,发向MDICLIENT窗口和MDI子窗口的消息由主框架窗
    口负责传递。这样,我们可以在主框架窗口截获关于MDICLIENT窗口的重画消息
    然后加入自己设计的代码。

    二、MDI客户窗口编程方法

    对MDI客户窗口编程有一定的难度。原因是MDIFrameWnd的客户区完全被
    MDICLIENT窗口覆盖掉了。这样,MDI主窗口类MDIFrameWnd的背景色和光标都不
    起作用。同时,微软并不支持将MDICLIENT窗口作为子类,MDICLIENT窗口只能使
    用标准的背景色和光标。所以,对MDI客户窗口编程不能象对普通窗口那样简单
    地重载WM_PAINT的消息处理函数。

    改变MDI客户窗口背景的方法有两种。

    使用CMDIFrameWnd::CreateClient 函数:
    CreateClient( LPCREATESTRUCT lpCreateStruct, CMenu* pWindowMenu );

    参数lpCreateStruct是指向CREATESTRUCT 结构的指针。在CREATESTRUCT 结构
    中lpszClass项指向窗口类WNDCLASS结构。通过改变WNDCLASS结构中的
    HbrBackground项和hCursor项可以更改MDICLIENT窗口的背景刷和光标。由于该
    函数创建新的MDICLIENT窗口对象,必须在重载的主窗口的OnCreate成员函数中
    调用。该方法比较复杂,必须手动创建MDI客户窗口,不能利用AppWizard自动
    提供的功能。而且,只能使用Windows95有限的16色背景刷。本文采用第二种方法。

    在主框架窗口的消息队列中截获发向MDI客户窗口的WM_PAINT消息并向主框架窗
    口发送一条标志消息。在这条标志消息的处理函数中对MDI客户窗口进行操作。
    该方法比较简捷,但有几点值得注意的地方。
    首先,如何截获MDI客户窗口WM_PAINT消息。MFC提供
    了PreTranslateMessage(MSG* pMsg) 函数。它在消息发送到TranslateMessage
    和DispatchMessage 函数以前预先解释消息。可以重载该函数截获MDI客户窗
    口WM_PAINT消息:

    BOOL PreTranslateMessage(MSG* pMsg)

    {

    if(pMsg->hwnd==m_hWndMDIClient && pMsg->message==WM_PAINT)

    PostMessage(WM_PAINT);

    return CMDIFrameWnd::PreTranslateMessage(pMsg);

    }

    其次,为简单起见,这里将标志消息设为MDI主窗口的WM_PAINT。在MDI主窗
    口WM_PAINT的消息处理函数中增加重画MDI客户窗口的代码。读者也可以自定义
    消息,不过麻烦一点。

    最后,由于对MDI客户窗口的操作都是在主窗口完成的。如何在主窗口中获得
    MDI客户窗口的设备描述表呢。其实,在MDI主窗口类中有MDI客户窗口成员
    m_hWndMDIClient。按如下方法得到客户窗口的设备描述表

    dc.m_hDC=::GetDC(this->m_hWndMDIClient);

    然后就可以对客户窗口进行操作了。

    三、实例

    将客户窗口设为256色背景。
    使用AppWizard生成MDI应用TEST。
    在TEST.CPP中的函数,增加如下代码:
    BOOL CTestApp::InitInstance()

    {

    AfxEnableControlContainer();

    #ifdef _AFXDLL

    Enable3dControls(); // Call this when using MFC in a shared DLL

    #else

    Enable3dControlsStatic(); // Call this when linking to MFC statically

    #endif

    SetRegistryKey(_T("Local AppWizard-Generated Applications"));

    LoadStdProfileSettings(); // Load standard INI file options (including MRU)

    CMultiDocTemplate* pDocTemplate;

    pDocTemplate = new CMultiDocTemplate(

    IDR_TESTTYPE,

    RUNTIME_CLASS(CTestDoc),

    RUNTIME_CLASS(CChildFrame), // custom MDI child frame

    RUNTIME_CLASS(CTestView));

    AddDocTemplate(pDocTemplate);

    // create main MDI Frame window

    CMainFrame* pMainFrame = new CMainFrame;

    if (!pMainFrame->LoadFrame(IDR_MAINFRAME))

    return FALSE;

    m_pMainWnd = pMainFrame;

    // Parse command line for standard shell commands, DDE, file open

    CCommandLineInfo cmdInfo;
  • 大家在使用某些软件的过程中,有没有注意到有些软件有一些很有趣的东西。
    比如说在主窗口的标题栏上居然有一个按钮。在Internet中随处可见这样的小
    控件。按钮怎么可以加入到非客户区(Client)呢?
    在这里,最关键的一点就是,大家不要被传统知识误导:真的认为它是
    一个按钮。有名柄(handle)的控件当然不能放在标题栏上了。有经验的程序员
    用Spy++跟踪一下的话,马上就会发现其中的秘密。它并不是一个按钮,只不
    过是处理成按钮的样子罢了。
    既然知道了所以然,那么我们为什么不能自己来做一个呢,当然没问题,下面我们就用
    Delphi来实现它,讲注意我的注解。
    在具体实例之前,我们应该知道几个关于标题栏的重要的消息:
    WM_NCPAINT:重画标题栏消息。我们必须截住它,可以在这里重画按钮;
    WM_NCLBUTTONDOWN:在标题栏上按下鼠标左键消息。我们可以截住它,在标题栏上画出
    按钮按下的样子,并且可以在其中进行自已的单击事件的处理,使得它像一个按钮;
    WM_NCLBUTTONUP:在标题栏上释放鼠标左键消息。我们可以截住它,在标题栏上画出按
    钮弹起的样子;
    WM_NCLBUTTONDBLCLK:在标题栏上双击鼠标左键消息。我们可以截住它,当在按钮区域
    双击时,我们就该使其无效,从而避免窗体执行最大化和还原操作。
    WM_NCRBUTTONDOWN:在标题栏上按下鼠标右键消息。我们可以截住它,当在按钮区域双
    击时,我们就该使其无效,从而避免弹出窗体按制菜单。
    WM_NCMOUSEMOVE:在标题栏上移动鼠标消息。我们可以截住它,当鼠标移出按钮区域
    时,我们就必须画出按钮没有被按下,即凸起时的样子。
    WM_NCACTIVATE:当标题栏在激活与非激活之间切换时收到该消息。我们可以截住它,
    当该窗口处理激活状态时,我们可以做一些事情,比如说将我们的标题栏按钮上的字体
    变灰或变黑来指示该窗口的当前状态。下面我没有加入该项功能,如果大家感兴趣的
    话,可以自己完成。
    (大家从这里可以发现,标题栏的消息都是WM_NC开头的)
    实例文件如下:
    unit main;

    interface

    uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    StdCtrls, Menus;

    type
    TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);

    private
    { Private declarations }

    CBBtnRect: TRect; // Caption Bar Button Rectangle
    CBBtnFont: TFont; // Caption Bar Button Font
    procedure DrawCaptionBtn(uEdge: UINT);
    // 当在标题栏上按下鼠标左按钮时进入该过程
    procedure WMNcLButtonDown(var m: TMessage); message WM_NCLBUTTONDOWN;
    // 当在标题栏上放开鼠标左按钮时进入该过程
    procedure WMNcLButtonUp(var m: TMessage); message WM_NCLBUTTONUP;
    // 当在标题栏上移动鼠标时进入该过程
    procedure WMNcMouseMove(var m: TMessage); message WM_NCMOUSEMOVE;
    // 当在标题栏上双击鼠标左铵钮时进入该过程
    procedure WMNcLButtonDBLClk(var m: TMessage); message WM_NCLBUTTONDBLCLK;
    // 当在标题栏上按下鼠标右按钮时进入该过程
    procedure WMNcRButtonDown(var m: TMessage); message WM_NCRBUTTONDOWN;
    // 当画标题栏时进入该过程
    procedure WMNcPaint(var m: TMessage); message WM_NCPAINT;
    // 当标题栏在激活与非激活之间切换时进入该过程
    procedure WMNcActivate(var m: TMessage); message WM_NCACTIVATE;
    public
    { Public declarations }
    end;

    var
    Form1: TForm1;

    implementation

    {$R *.DFM}

    procedure TForm1.DrawCaptionBtn(uEdge: UINT);
    var
    hCaptionDC: HDC; // 标题条Device Context
    hOldFont: HFONT; // 原来的字体
    r: TRect;
    begin
    hCaptionDC := GetWindowDC(Self.Handle); // 注意不能用GetDC,那样的话,将得不
    到标题栏
    // 的设备上下文

    file://画按钮的样子,如果uEdge=EDGE_RAISED,则画出的样子为凸起;如果
    file://uEdge=EDGE_SUNKEN,则画出的样子为凹下。
    DrawEdge(hCaptionDC, CBBtnRect, uEdge, BF_RECT or BF_MIDDLE or
    BF_SOFT);

    file://设置标题栏的设备上下文为透明状态
    SetBkMode(hCaptionDC, TRANSPARENT);

    file://设置标题栏设备上下文的字体
    hOldFont:= SelectObject(hCaptionDC, CBBtnFont.Handle);

    file://画按钮
    if uEdge = EDGE_RAISED then
    DrawText(hCaptionDC, ’Caption Bar Button’, 18, CBBtnRect, DT_CENTER)
    else begin
    r := CBBtnRect;
    OffsetRect(r, 1, 1);
    DrawText(hCaptionDC, ’Caption Bar Button’, 18, r, DT_CENTER);
    end;

    file://还原为原来的字体
    SelectObject(hCaptionDC, hOldFont);
    end;

    procedure TForm1.WMNcActivate(var m: TMessage);
    begin
    inherited;
    DrawCaptionBtn(EDGE_RAISED);
    end;


    procedure TForm1.WMNcPaint(var m: TMessage);
    begin
    inherited;
    DrawCaption
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页