用SkinMagic工具包创建换皮肤程序
作者:北京林业大学 李少杰

下载本文示例工程

代码运行效果图如下:




以前看到瑞星和金山词霸有换皮肤功能,很是羡慕,自己也想做个换皮肤的程序,但毕竟自己能力有限,以前在此处看到一篇介绍换皮肤功能的文章,但只能应用在对话框程序上。我也试过像BCGCBar这样的大型软件,给我的感觉是软件老是出问题,应用起来也很复杂。在年前我就看到本站有SkinMagic软件包,但当时不知怎么用,发了贴子也没人回答。可能是很少有人用的缘故吧,本站也未更新此软件包。偶然的一次机会,让我知道了怎样来用它,
而且编译出来的软件体积很小,所以在这里向大家介绍一下,希望对大家有些帮助。
如何使用:
① 新建工程后,将SkinMagic包中的SkinMagicLib.h、SkinMagicLib.lib、DETOURS.lib拷贝到所在文件夹下,点击Project->Add To Project->Files,包含到工程中。
② 在Resource View中点击Import 菜单引入几个Skin二进制文件放在相同的“SKINMAGIC”文件夹下,分别给起一个名字(见源程序)。
③ 在stdafx.h中加入#include "SkinMagicLib.h"
④ 在此处添加如下内容

if (!ProcessShellCommand(cmdInfo))
return FALSE;
/////////////////////////////////////////////////////////////////////////////////////////
VERIFY( 1 == InitSkinMagicLib( AfxGetInstanceHandle(), "Demo" ,NULL,NULL ) );//初始化类库
VERIFY( 1 == LoadSkinFromResource( AfxGetInstanceHandle()  , "KROMO" ,"SKINMAGIC") );//从资源中加载皮肤
VERIFY( 1 == SetWindowSkin( m_pMainWnd->m_hWnd , "MainFrame" ));
VERIFY( 1 == SetDialogSkin( "Dialog" ) );
((CMainFrame*)m_pMainWnd)->m_bSkinned = TRUE;
((CMainFrame*)m_pMainWnd)->m_nIndex = 0;
////////////////////////////////////////////////////////////////////////////////////////////
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
⑤ 在MainFrame中加入换皮肤菜单,及相应的函数,在函数中加载皮肤。
void CMainFrame::OnDevior()
{
LoadSkinFromResource( AfxGetInstanceHandle()  , "DEVIOR" ,"SKINMAGIC") ;
}
到此为止,完美的换皮肤程序就出来了。
posted @ 2009-05-21 09:32 wrh 阅读(254) | 评论 (0)编辑 收藏

文章导读:  [准备工作]  1、将SkinMagicTrial.dll放置在调试目录  2、设置库文件目录,在项目[连接器]的[附加依赖项]中加入库SkinMagicTrial.lib  3、在项目的stdafx.h文件中加入头文件 #include "SkinMagicLib.h"  [创建过程]  1、初始化SkinMagic库: 

  [准备工作]

  1、将SkinMagicTrial.dll放置在调试目录

  2、设置库文件目录,在项目[连接器]的[附加依赖项]中加入库SkinMagicTrial.lib

  3、在项目的stdafx.h文件中加入头文件 #include "SkinMagicLib.h"

  [创建过程]

  1、初始化SkinMagic库:

  要使用SkinMagic,这一步必不可少。在应用程序类的InitInstance()函数中行加入如下代码(粗体部分):

  CWinApp::InitInstance();

  VERIFY( 1 == InitSkinMagicLib(AfxGetInstanceHandle(), NULL ,

  NULL, NULL ));

  说明:

  int InitSkinMagicLib( //初始化SkinMagic工具库
  HINSTANCE hInstance, //应用程序句柄
  char* szApplication , //皮肤文件中定义的应用程序名,置为NULL即可
  char* szRegCode, //SkinMagic的使用注册码。若无置为NULL
  char* szReserved2 //保留位,为NULL
  );

  2、调入皮肤文件:

  皮肤的调用有两种方法,一是直接从皮肤文件中调用,另一种方法是从资源文件中调用,分别说明如下:

  1)从皮肤文件中调用皮肤:紧接上句,加入如下代码

  VERIFY( 1 == LoadSkinFile("corona.smf"));

  2)从资源文件中调用皮肤:

  VERIFY(1 == LoadSkinFromResource(NULL,"FUTURA","skin"));

  说明:

int LoadSkinFromResource(
  HMODULE hModule, //包含皮肤文件的模块句柄,若NULL表面在本模块中
 char* lpSkinName , //皮肤资源的名称
  char* lpType); //资源的类型

  3、为窗口添加皮肤:

  1)为标准窗口(拥有标题栏、系统菜单、可变大小等特征,比如文档/视图结构和有菜单的对话框)添加皮肤,通常用于主窗口。在应用程序类的InitInstance()函数的底部加入如下代码:

  VERIFY( 1 == SetWindowSkin( m_pMainWnd->m_hWnd , "MainFrame" ));

  m_pMainWnd->ShowWindow(SW_SHOW);

  m_pMainWnd->UpdateWindow();

  说明:

  int SetWindowSkin(
  HWND hWnd, //要使用皮肤的窗口句柄
  char* lpSkinName //为skinFrameWnd对象指定的名称
  );

  2)为对话框添加皮肤

  在对话框显示之前调用,通常在应用程序初始化函数中调用

  VERIFY( 1 == SetWindowSkin( m_pMainWnd->m_hWnd , "MainFrame" ));

  VERIFY( 1 == SetDialogSkin( "Dialog" ) );

  m_pMainWnd->ShowWindow(SW_SHOW);

  m_pMainWnd->UpdateWindow();

  说明:

  int SetDialogSkin(
  char* lpSkinName //为skinFrameWnd对象指定的名称
  );
  使用该函数后,以后程序创建的对话框都将使用该皮肤,但对话框大小不可变。
3)为单个对话框窗口添加皮肤,例如在对话框视图中:重载对话框视图的创建函数OnCreate,加入如下代码:

  VERIFY( 1 == SetSingleDialogSkin( m_hWnd, "Dialog" ) );

  EnableWindowScrollbarSkin( m_hWnd , SB_BOTH );

  说明:

  int SetSingleDialogSkin(
  HWND hWnd, //要使用皮肤的窗口句柄
  char* lpSkinName //为skinFrameWnd对象指定的名称
  );
  int EnableWindowScrollbarSkin( //为滚动条添加皮肤
  HWND hWnd, //要使用皮肤的窗口句柄
  int* fnBar //要使用皮肤的滚动条,SB_BOTH表明是横竖都是用皮肤
  );

  4、释放SkinMagic资源
 
  重载应用程序的ExitInstance()函数,添加如下代码:

  ExitSkinMagicLib();

posted @ 2009-05-20 20:57 wrh 阅读(437) | 评论 (0)编辑 收藏
如果n <= 0,返回NULL
如果n == 1,返回"",也就是一个空串。
如果成功返回值等于string,也就是获得的字符串的首地址。
如果出错,或者读到FILE的结尾,返回NULL.

以下是这个函数的实现:

/***
*fgets.c - get string from a file
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
* defines fgets() - read a string from a file
*
*******************************************************************************/

#include <cruntime.h>
#include <stdio.h>
#include <dbgint.h>
#include <file2.h>
#include <internal.h>
#include <mtdll.h>
#include <tchar.h>

/***
*char *fgets(string, count, stream) - input string from a stream
*
*Purpose:
* get a string, up to count-1 chars or '\n', whichever comes first,
* append '\0' and put the whole thing into string. the '\n' IS included
* in the string. if count<=1 no input is requested. if EOF is found
* immediately, return NULL. if EOF found after chars read, let EOF
* finish the string as '\n' would.
*
*Entry:
* char *string - pointer to place to store string
* int count - max characters to place at string (include \0)
* FILE *stream - stream to read from
*
*Exit:
* returns string with text read from file in it.
* if count <= 0 return NULL
* if count == 1 put null string in string
* returns NULL if error or end-of-file found immediately
*
*Exceptions:
*
*******************************************************************************/

_TSCHAR * __cdecl _fgetts (
_TSCHAR *string,
int count,
FILE *str
)
{
REG1 FILE *stream;
REG2 _TSCHAR *pointer = string;
_TSCHAR *retval = string;
int ch;

_VALIDATE_RETURN(( string != NULL ) || ( count == 0 ), EINVAL, NULL);
_VALIDATE_RETURN(( count >= 0 ), EINVAL, NULL);
_VALIDATE_RETURN(( str != NULL ), EINVAL, NULL);

if (count == 0)
{
return NULL;
}

/* The C Standard states the input buffer should remain
unchanged if EOF is encountered immediately. Hence we
do not blank out the input buffer here */

/* Init stream pointer */
stream = str;

_lock_str(stream);
__try {

#ifndef _UNICODE
_VALIDATE_STREAM_ANSI_SETRET(stream, EINVAL, retval, NULL);
#endif /* _UNICODE */
if(retval!=NULL)
{
while (--count)
{
if ((ch = _fgettc_nolock(stream)) == _TEOF)
{
if (pointer == string) {
retval=NULL;
goto done;
}

break;
}

if ((*pointer++ = (_TSCHAR)ch) == _T('\n'))
break;
}
*pointer = _T('\0');
}


/* Common return */
done:

; }
__finally {
_unlock_str(stream);
}

return(retval);
}
posted @ 2009-05-20 09:22 wrh 阅读(739) | 评论 (0)编辑 收藏
F1: 帮助 
Ctrl+O :Open 
Ctrl+P :Print 
Ctrl+N :New 
Ctrl+Shift+F2 :清除所有书签 
F2 :上一个书签 
Shift+F2 :上一个书签 
Alt+F2 :编辑书签 
Ctrl+F2 :添加/删除一个书签 
F12 :Goto definition 
Shift+F12 :Goto reference 
Ctrl+'Num+' :Displays the next symbol definition or reference 
Ctrl+'Num-' :Displays the previous symbol definition or reference 
Ctrl+J/K :寻找上一个/下一个预编译条件 
Ctrl+Shift+J/K :寻找上一个/下一个预编译条件并将这一块选定 
Ctrl+End :文档尾 
Ctrl+Shift+End :选定从当前位置到文档尾 
Ctrl+Home :文档头 
Ctrl+Shift+Home :选定从当前位置到文档头 
Ctrl+B/Alt+F9 :编辑断点 
Alt+F3/Ctrl+F :查找 
F3 :查找下一个 
Shift+F3 :查找上一个 
Ctrl+]/Ctrl+E :寻找下一半括弧 
Ctrl+Shift+] :寻找下一半括弧并选定括弧之间的部分(包括括弧) 
Ctrl+Shift+E :寻找下一半括弧并选定括弧之间的部分(包括括弧) 
F4 :寻找下一个错误/警告位置 
Shift+F4 :寻找上一个错误/警告位置 
Shift+Home :选定从当前位置到行首 
Shift+End :选定从当前位置到行尾 
Ctrl+L :剪切当前行 
Ctrl+Shift+L :删除当前行 
Alt+Shift+T :交换当前行和上一行 
Ctrl+Alt+T :Brings up the completion list box 
Shift+PageDown :选定从当前位置到下一页当前位置 
Shift+PageUp :选定从当前位置到上一页当前位置 
Ctrl+Shift+Space:显示函数参数的Tooltip 
Ctrl+Z/Alt+Backspace :Undo 
Ctrl+Shift+Z/Ctrl+Y :Redo 
F8 :当前位置变成选定区域的头/尾(再移动光标或者点鼠标就会选定) 
Ctrl+Shift+F8 :当前位置变成矩形选定区域的头/尾(再移动光标或者点鼠标就会选定) 
Alt+F8 :自动格式重排 
Ctrl+G :Goto 
Ctlr+X/Shift+Del:Cut 
Ctrl+C/Ctrl+Ins :Copy 
Ctrl+V/Shift+Ins:Paste 
Ctrl+U :将选定区域转换成小写 
Ctrl+Shift+U :将选定区域转换成大写 
Ctrl+F8 :当前行变成选定区域的头/尾(再移动上下光标或者点鼠标就会选定多行) 
Ctrl+Shift+L :删除从当前位置到行尾 
Ctrl+Shift+8 :将所有Tab变成`或者还原 
Ctrl+T :显示变量类型 
Ctrl+↑ :向上滚屏 
Ctrl+↓ :向下滚屏 
Ctrl+Del :删除当前单词的后半截(以光标为分割) 
Ctrl+Backspace :删除当前单词的前半截(以光标为分割) 
Ctrl+← :移到前一个单词 
Ctrl+→ :移到后一个单词 
Ctrl+Shift+← :选定当前位置到前一个单词 
Ctrl+Shift+→ :选定当前位置到后一个单词 
Ctrl+Shift+T :将本单词和上一个单词互换
posted @ 2009-04-24 15:48 wrh 阅读(245) | 评论 (0)编辑 收藏
数学函数,所在函数库为math.h、stdlib.h、string.h、float.h    
  ----------------------------------------------------------------------------------------------------------  
   
   
  int   abs(int   i)   返回整型参数i的绝对值    
  double   cabs(struct   complex   znum)   返回复数znum的绝对值    
  double   fabs(double   x)   返回双精度参数x的绝对值    
  long   labs(long   n)   返回长整型参数n的绝对值    
  double   exp(double   x)   返回指数函数ex的值    
  double   frexp(double   value,int   *eptr)   返回value=x*2n中x的值,n存贮在eptr中    
  double   ldexp(double   value,int   exp);   返回value*2exp的值    
  double   log(double   x)   返回logex的值    
  double   log10(double   x)   返回log10x的值    
  double   pow(double   x,double   y)   返回xy的值    
  double   pow10(int   p)   返回10p的值    
  double   sqrt(double   x)   返回x的开方    
  double   acos(double   x)   返回x的反余弦cos-1(x)值,x为弧度    
  double   asin(double   x)   返回x的反正弦sin-1(x)值,x为弧度    
  double   atan(double   x)   返回x的反正切tan-1(x)值,x为弧度    
  double   atan2(double   y,double   x)   返回y/x的反正切tan-1(x)值,y的x为弧度    
  double   cos(double   x)   返回x的余弦cos(x)值,x为弧度    
  double   sin(double   x)   返回x的正弦sin(x)值,x为弧度    
  double   tan(double   x)   返回x的正切tan(x)值,x为弧度    
  double   cosh(double   x)   返回x的双曲余弦cosh(x)值,x为弧度    
  double   sinh(double   x)   返回x的双曲正弦sinh(x)值,x为弧度    
  double   tanh(double   x)   返回x的双曲正切tanh(x)值,x为弧度    
  double   hypot(double   x,double   y)   返回直角三角形斜边的长度(z),   x和y为直角边的长度,z2=x2+y2    
  double   ceil(double   x)   返回不小于x的最小整数    
  double   floor(double   x)   返回不大于x的最大整数    
  void   srand(unsigned   seed)   初始化随机数发生器    
  int   rand()   产生一个随机数并返回这个数    
  double   poly(double   x,int   n,double   c[])   从参数产生一个多项式    
  double   modf(double   value,double   *iptr)   将双精度数value分解成尾数和阶    
  double   fmod(double   x,double   y)   返回x/y的余数    
  double   frexp(double   value,int   *eptr)   将双精度数value分成尾数和阶    
  double   atof(char   *nptr)   将字符串nptr转换成浮点数并返回这个浮点数    
  double   atoi(char   *nptr)   将字符串nptr转换成整数并返回这个整数    
  double   atol(char   *nptr)   将字符串nptr转换成长整数并返回这个整数    
  char   *ecvt(double   value,int   ndigit,int   *decpt,int   *sign)    
  将浮点数value转换成字符串并返回该字符串    
  char   *fcvt(double   value,int   ndigit,int   *decpt,int   *sign)    
  将浮点数value转换成字符串并返回该字符串    
  char   *gcvt(double   value,int   ndigit,char   *buf)    
  将数value转换成字符串并存于buf中,并返回buf的指针    
  char   *ultoa(unsigned   long   value,char   *string,int   radix)    
  将无符号整型数value转换成字符串并返回该字符串,radix为转换时所用基数    
  char   *ltoa(long   value,char   *string,int   radix)    
  将长整型数value转换成字符串并返回该字符串,radix为转换时所用基数    
  char   *itoa(int   value,char   *string,int   radix)    
  将整数value转换成字符串存入string,radix为转换时所用基数    
  double   atof(char   *nptr)   将字符串nptr转换成双精度数,并返回这个数,错误返回0    
  int   atoi(char   *nptr)   将字符串nptr转换成整型数,   并返回这个数,错误返回0    
  long   atol(char   *nptr)   将字符串nptr转换成长整型数,并返回这个数,错误返回0    
  double   strtod(char   *str,char   **endptr)将字符串str转换成双精度数,并返回这个数,    
  long   strtol(char   *str,char   **endptr,int   base)将字符串str转换成长整型数,   并返回这个数,    
  int   matherr(struct   exception   *e)   用户修改数学错误返回信息函数(没有必要使用)    
  double   _matherr(_mexcep   why,char   *fun,double   *arg1p,   double   *arg2p,double   retval)    
  用户修改数学错误返回信息函数(没有必要使用)    
  unsigned   int   _clear87()   清除浮点状态字并返回原来的浮点状态    
  void   _fpreset()   重新初使化浮点数学程序包    
  unsigned   int   _status87()   返回浮点状态字
posted @ 2009-04-15 10:03 wrh 阅读(489) | 评论 (0)编辑 收藏

在VC++6.0中missing function header (old-style formal list?)解决方法

该函数的函数头后边加了分号

去掉即可

例如:void fun();{}就会出现那种情况

posted @ 2009-03-27 10:37 wrh 阅读(1954) | 评论 (0)编辑 收藏

VC中没有现成的函数来选择一个文件夹,但这是经常会用到的,怎么办?
自动动手,丰衣足食!

使用SHBrowseForFolder,代码如下:

#include
  
int SelFolder(HWND hParent, CString &strFolder)
{
    strFolder.Empty();
 
    LPMALLOC lpMalloc;
 
    if (::SHGetMalloc(&lpMalloc) != NOERROR) return 0;
 
    char szDisplayName[_MAX_PATH];
    char szBuffer[_MAX_PATH];
    BROWSEINFO browseInfo;
    browseInfo.hwndOwner = hParent;
    browseInfo.pidlRoot = NULL; // set root at Desktop
    browseInfo.pszDisplayName = szDisplayName;
    browseInfo.lpszTitle = "Select a folder";
    browseInfo.ulFlags = BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS;
    browseInfo.lpfn = NULL;
    browseInfo.lParam = 0;
 
    LPITEMIDLIST lpItemIDList;
    if ((lpItemIDList = ::SHBrowseForFolder(&browseInfo)) != NULL)
    {
        // Get the path of the selected folder from the    item ID list.
        if (::SHGetPathFromIDList(lpItemIDList, szBuffer))
        {
            // At this point, szBuffer contains the path the user chose.
            if (szBuffer[0] == ´\0´) return 0;
 
            // We have a path in szBuffer! Return it.
            strFolder = szBuffer;
            return 1;
        }
        else return 1; // strResult is empty
 
        lpMalloc->Free(lpItemIDList);
        lpMalloc->Release();
    }
   
 return 1;
}

 

//////调用:

void CMusic1Dlg::OnOK()
{
 // TODO: Add extra validation here
 CString str;

 HWND m_hWnd = GetSafeHwnd();
 
 SelFolder(m_hWnd,str);

 m_list.AddString(str);
// CDialog::OnOK();
}

//------------------------------------------------------------------------------------------------------

//_________________________________________________________________

“选择文件夹”对话框的封装


我们经常需要用到“选择文件夹”对话框,相应的API已经很好用,但稍嫌麻烦,所以我专门将其封装了一下,力求一步到位。

函数封装如下:
/*****************************************************************
** 函数名:GetPath
** 输 入: 无
** 输 出: CString strPath
**        strPath非空, 表示用户选择的文件夹路径
**        strPath为空, 表示用户点击了“取消”键,取消选择
** 功能描述:显示“选择文件夹”对话框,让用户选择文件夹
****************************************************************/

CString GetPath()
{
 CString strPath = "";
 BROWSEINFO bInfo;
 ZeroMemory(&bInfo, sizeof(bInfo));
 bInfo.hwndOwner = m_hWnd;
 bInfo.lpszTitle = _T("请选择路径: ");
 bInfo.ulFlags = BIF_RETURNONLYFSDIRS;   
 
 LPITEMIDLIST lpDlist; //用来保存返回信息的IDList
 lpDlist = SHBrowseForFolder(&bInfo) ; //显示选择对话框
 if(lpDlist != NULL)  //用户按了确定按钮
 {
  TCHAR chPath[255]; //用来存储路径的字符串
  SHGetPathFromIDList(lpDlist, chPath);//把项目标识列表转化成字符串
  strPath = chPath; //将TCHAR类型的字符串转换为CString类型的字符串
 }
 return strPath;
}

调用时只需要用到以下代码:
CString strPath = GetPath();
则strPath为用户选择的文件夹路径。如果用户点击了对话框的取消键,则strPath为空字符串("");

posted @ 2009-03-14 10:02 wrh 阅读(5204) | 评论 (1)编辑 收藏
char   path[MAX_PATH];  
  GetModuleFileName(NULL,path,MAX_PATH);

GetModuleFileName(   AfxGetInstanceHandle(),szPath,MAX_PATH);


GetModuleFileNameEx  
  The   GetModuleFileNameEx   function   retrieves   the   fully   qualified   path   for   the   specified   module.    
   
  DWORD   GetModuleFileNameEx(  
      HANDLE   hProcess,         //   handle   to   the   process  
      HMODULE   hModule,         //   handle   to   the   module  
      LPTSTR   lpFilename,     //   buffer   that   receives   the   path  
      DWORD   nSize                   //   size   of   the   buffer  
  );  
     
  Parameters  
  hProcess    
  Handle   to   the   process   that   contains   the   module.    
  hModule    
  Handle   to   the   module.    
  lpFilename    
  Pointer   to   the   buffer   that   receives   the   fully   qualified   path   to   the   module.    
  nSize    
  Specifies   the   size,   in   bytes,   of   the   lpFilename   buffer.    
  Return   Value  
  If   the   function   succeeds,   the   return   value   specifies   the   length   of   the   string   copied   to   the   buffer.    
   
  If   the   function   fails,   the   return   value   is   zero.   To   get   extended   error   information,   call   GetLastError.    
   
  See   Also  
  Process   Status   Helper   Overview,   PSAPI   Functions,   EnumProcesses,   GetModuleBaseName     
   
posted @ 2009-03-14 10:00 wrh 阅读(1461) | 评论 (0)编辑 收藏

文件的打开(fopen函数)

fopen函数用来打开一个文件,其调用的一般形式为:
文件指针名=fopen(文件名,使用文件方式);
其中,
“文件指针名”必须是被说明为FILE 类型的指针变量;
“文件名”是被打开文件的文件名;
“使用文件方式”是指文件的类型和操作要求。
“文件名”是字符串常量或字符串数组。
例如:
FILE *fp;
fp=("file a","r");
其意义是在当前目录下打开文件file a,只允许进行“读”操作,并使fp指向该文件。
又如:
FILE *fphzk
fphzk=("c:\\hzk16","rb")
其意义是打开C驱动器磁盘的根目录下的文件hzk16,这是一个二进制文件,只允许按二进制方式进行读操作。两个反斜线“\\ ”中的第一个表示转义字符,第二个表示根目录。

使用文件的方式共有12种,下面给出了它们的符号和意义。
文件使用方式 意义
“rt” 只读打开一个文本文件,只允许读数据
“wt” 只写打开或建立一个文本文件,只允许写数据
“at” 追加打开一个文本文件,并在文件末尾写数据
“rb” 只读打开一个二进制文件,只允许读数据
“wb” 只写打开或建立一个二进制文件,只允许写数据
“ab” 追加打开一个二进制文件,并在文件末尾写数据
“rt+” 读写打开一个文本文件,允许读和写
“wt+” 读写打开或建立一个文本文件,允许读写
“at+” 读写打开一个文本文件,允许读,或在文件末追加数据
“rb+” 读写打开一个二进制文件,允许读和写
“wb+” 读写打开或建立一个二进制文件,允许读和写
“ab+” 读写打开一个二进制文件,允许读,或在文件末追加数据

对于文件使用方式有以下几点说明:
1) 文件使用方式由r,w,a,t,b,+六个字符拼成,各字符的含义是:
r(read): 读
w(write): 写
a(append): 追加
t(text): 文本文件,可省略不写
b(banary): 二进制文件
+: 读和写
2) 凡用“r”打开一个文件时,该文件必须已经存在,且只能从该文件读出。
3) 用“w”打开的文件只能向该文件写入。若打开的文件不存在,则以指定的文件名建立该文件,若打开的文件已经存在,则将该文件删去,重建一个新文件。
4) 若要向一个已存在的文件追加新的信息,只能用“a”方式打开文件。但此时该文件必须是存在的,否则将会出错。
5) 在打开一个文件时,如果出错,fopen将返回一个空指针值NULL。在程序中可以用这一信息来判别是否完成打开文件的工作,并作相应的处理。因此常用以下程序段打开文件:
6) if((fp=fopen("c:\\hzk16","rb")==NULL)
{
printf("\nerror on open c:\\hzk16 file!");
getch();
exit(1);
}

这段程序的意义是,如果返回的指针为空,表示不能打开C盘根目录下的hzk16文件,则给出提示信息“error on open c:\ hzk16 file!”,下一行getch()的功能是从键盘输入一个字符,但不在屏幕上显示。在这里,该行的作用是等待,只有当用户从键盘敲任一键时,程序才继续执行,因此用户可利用这个等待时间阅读出错提示。敲键后执行exit(1)退出程序。

7) 把一个文本文件读入内存时,要将ASCII码转换成二进制码,而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此文本文件的读写要花费较多的转换时间。对二进制文件的读写不存在这种转换。

8) 标准输入文件(键盘),标准输出文件(显示器),标准出错输出(出错信息)是由系统打开的,可直接使用。

posted @ 2009-03-13 13:08 wrh 阅读(9262) | 评论 (0)编辑 收藏
队列消息和非队列消息
   
从消息的发送途径来看,消息可以分成2种:队列消息和非队列消息。消息队列由可以分成系统消息队列和线程消息队列。系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI现成创建消息队列,所有线程产生时并没有消息队列,仅当线程第一次调用GDI函数数系统给线程创建一个消息队列。队列消息送到系统消息队列,然后到线程消息队列;非队列消息直接送给目的窗口过程。
     对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到系统消息队列,由Windows系统去进行处理。Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队列操心了,Windows开始忙自己的事情去了。线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的窗口过程去处理。
     一般来讲,系统总是将消息Post在消息队列的末尾。这样保证窗口以先进先出的顺序接受消息。然而,WM_PAINT是一个例外,同一个窗口的多个 WM_PAINT被合并成一个 WM_PAINT 消息, 合并所有的无效区域到一个无效区域。合并WM_PAIN的目的是为了减少刷新窗口的次数。
    非队列消息将会绕过系统队列和消息队列,直接将消息发送到窗口过程,。系统发送非队列消息通知窗口,系统发送消息通知窗口。 例如,当用户激活一个窗口系统发送WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR。这些消息通知窗口它被激活了。非队列消息也可以由当应用程序调用系统函数产生。例如,当程序调用SetWindowPos系统发送WM_WINDOWPOSCHANGED消息。一些函数也发送非队列消息,例如下面我们要谈到的函数。
    
消息的发送
     了解了上面的这些基础理论之后,我们就可以进行一下简单的消息发送与接收。
     把一个消息发送到窗口有3种方式:发送、寄送和广播。
     发送消息的函数有SendMessage、SendMessageCallback、SendNotifyMessage、SendMessageTimeout;寄送消息的函数主要有PostMessage、PostThreadMessage、PostQuitMessage;广播消息的函数我知道的只有BroadcastSystemMessage、BroadcastSystemMessageEx。
     SendMessage的原型如下:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),这个函数主要是向一个或多个窗口发送一条消息,一直等到消息被处理之后才会返回。不过需要注意的是,如果接收消息的窗口是同一个应用程序的一部分,那么这个窗口的窗口函数就被作为一个子程序马上被调用;如果接收消息的窗口是被另外的线程所创建的,那么窗口系统就切换到相应的线程并且调用相应的窗口函数,这条消息不会被放进目标应用程序队列中。函数的返回值是由接收消息的窗口的窗口函数返回,返回的值取决于被发送的消息。
     PostMessage的原型如下:BOOL PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam),该函数把一条消息放置到创建hWnd窗口的线程的消息队列中,该函数不等消息被处理就马上将控制返回。需要注意的是,如果hWnd参数为HWND_BROADCAST,那么,消息将被寄送给系统中的所有的重叠窗口和弹出窗口,但是子窗口不会收到该消息;如果hWnd参数为NULL,则该函数类似于将dwThreadID参数设置成当前线程的标志来调用PostThreadMEssage函数。
  从上面的这2个具有代表性的函数,我们可以看出消息的发送方式和寄送方式的区别所在:被发送的消息是否会被立即处理,函数是否立即返回。被发送的消息会被立即处理,处理完毕后函数才会返回;被寄送的消息不会被立即处理,他被放到一个先进先出的队列中,一直等到应用程序空线的时候才会被处理,不过函数放置消息后立即返回。
  实际上,发送消息到一个窗口处理过程和直接调用窗口处理过程之间并没有太大的区别,他们直接的唯一区别就在于你可以要求操作系统截获所有被发送的消息,但是不能够截获对窗口处理过程的直接调用。
  以寄送方式发送的消息通常是与用户输入事件相对应的,因为这些事件不是十分紧迫,可以进行缓慢的缓冲处理,例如鼠标、键盘消息会被寄送,而按钮等消息则会被发送。
  广播消息用得比较少,BroadcastSystemMessage函数原型如下:
      long BroadcastSystemMessage(DWORD dwFlags,LPDWORD lpdwRecipients,UINT uiMessage,WPARAM wParam,LPARAM lParam);该函数可以向指定的接收者发送一条消息,这些接收者可以是应用程序、可安装的驱动程序、网络驱动程序、系统级别的设备驱动消息和他们的任意组合。需要注意的是,如果dwFlags参数是BSF_QUERY并且至少一个接收者返回了BROADCAST_QUERY_DENY,则返回值为0,如果没有指定BSF_QUERY,则函数将消息发送给所有接收者,并且忽略其返回值。

消息的接收
 
消息的接收主要有3个函数:GetMessage、PeekMessage、WaitMessage。
  GetMessage原型如下:BOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax);该函数用来获取与hWnd参数所指定的窗口相关的且wMsgFilterMin和wMsgFilterMax参数所给出的消息值范围内的消息。需要注意的是,如果hWnd为NULL,则GetMessage获取属于调用该函数应用程序的任一窗口的消息,如果wMsgFilterMin和wMsgFilterMax都是0,则GetMessage就返回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息,至于WM_PAINT则只有在其处理之后才被删除。
   PeekMessage原型如下:BOOL PeekMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg);该函数用于查看应用程序的消息队列,如果其中有消息就将其放入lpMsg所指的结构中,不过,与GetMessage不同的是,PeekMessage函数不会等到有消息放入队列时才返回。同样,如果hWnd为NULL,则PeekMessage获取属于调用该函数应用程序的任一窗口的消息,如果hWnd=-1,那么函数只返回把hWnd参数为NULL的PostAppMessage函数送去的消息。如果wMsgFilterMin和wMsgFilterMax都是0,则PeekMessage就返回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息,至于WM_PAINT则只有在其处理之后才被删除。
   WaitMessage原型如下:BOOL VaitMessage();当一个应用程序无事可做时,该函数就将控制权交给另外的应用程序,同时将该应用程序挂起,直到一个新的消息被放入应用程序的队列之中才返回。

消息的处理
  接下来我们谈一下消息的处理,首先我们来看一下VC中的消息泵:
while(GetMessage(&msg, NULL, 0, 0))
{
       if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg))
      {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
       }
}

   首先,GetMessage从进程的主线程的消息队列中获取一个消息并将它复制到MSG结构,如果队列中没有消息,则GetMessage函数将等待一个消息的到来以后才返回。 如果你将一个窗口句柄作为第二个参数传入GetMessage,那么只有指定窗口的的消息可以从队列中获得。GetMessage也可以从消息队列中过滤消息只接受消息队列中落在范围内的消息。这时候就要利用GetMessage/PeekMessage指定一个消息过滤器。这个过滤器是一个消息标识符的范围或者是一个窗体句柄,或者两者同时指定。当应用程序要查找一个后入消息队列的消息是很有用。WM_KEYFIRST 和 WM_KEYLAST 常量用于接受所有的键盘消息。 WM_MOUSEFIRST 和 WM_MOUSELAST 常量用于接受所有的鼠标消息。
 然后TranslateAccelerator判断该消息是不是一个按键消息并且是一个加速键消息,如果是,则该函数将把几个按键消息转换成一个加速键消息传递给窗口的回调函数。处理了加速键之后,函数TranslateMessage将把两个按键消息WM_KEYDOWN和WM_KEYUP转换成一个WM_CHAR,不过需要注意的是,消息WM_KEYDOWN,WM_KEYUP仍然将传递给窗口的回调函数。    
 处理完之后,DispatchMessage函数将把此消息发送给该消息指定的窗口中已设定的回调函数。如果消息是WM_QUIT,则GetMessage返回0,从而退出循环体。应用程序可以使用PostQuitMessage来结束自己的消息循环。通常在主窗口的WM_DESTROY消息中调用。
 下面我们举一个常见的小例子来说明这个消息泵的运用:
if (::PeekMessage(&msg, m_hWnd, WM_KEYFIRST,WM_KEYLAST, PM_REMOVE))
{
          if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)...
}
  这里我们接受所有的键盘消息,所以就用WM_KEYFIRST 和 WM_KEYLAST作为参数。最后一个参数可以是PM_NOREMOVE 或者 PM_REMOVE,表示消息信息是否应该从消息队列中删除。                
   所以这段小代码就是判断是否按下了Esc键,如果是就进行处理。

窗口过程
 窗口过程是一个用于处理所有发送到这个窗口的消息的函数。任何一个窗口类都有一个窗口过程。同一个类的窗口使用同样的窗口过程来响应消息。 系统发送消息给窗口过程将消息数据作为参数传递给他,消息到来之后,按照消息类型排序进行处理,其中的参数则用来区分不同的消息,窗口过程使用参数产生合适行为。
 一个窗口过程不经常忽略消息,如果他不处理,它会将消息传回到执行默认的处理。窗口过程通过调用DefWindowProc来做这个处理。窗口过程必须return一个值作为它的消息处理结果。大多数窗口只处理小部分消息和将其他的通过DefWindowProc传递给系统做默认的处理。窗口过程被所有属于同一个类的窗口共享,能为不同的窗口处理消息。下面我们来看一下具体的实例:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 int wmId, wmEvent;
 PAINTSTRUCT ps;
 HDC hdc;
 TCHAR szHello[MAX_LOADSTRING];
 LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

 switch (message)
 {
  case WM_COMMAND:
         wmId    = LOWORD(wParam);
         wmEvent = HIWORD(wParam);
         // Parse the menu selections:
         switch (wmId)
         {
          case IDM_ABOUT:
             DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
             break;
          case IDM_EXIT:
             DestroyWindow(hWnd);
             break;
          default:
             return DefWindowProc(hWnd, message, wParam, lParam);
         }
   break;

  case WM_PAINT:
         hdc = BeginPaint(hWnd, &ps);
         // TODO: Add any drawing code here...
         RECT rt;
         GetClientRect(hWnd, &rt);
         DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
         EndPaint(hWnd, &ps);
         break;

  case WM_DESTROY:
         PostQuitMessage(0);
         break;
  default:
         return DefWindowProc(hWnd, message, wParam, lParam);
  }
  return 0;
}

消息分流器
  通常的窗口过程是通过一个switch语句来实现的,这个事情很烦,有没有更简便的方法呢?有,那就是消息分流器,利用消息分流器,我们可以把switch语句分成更小的函数,每一个消息都对应一个小函数,这样做的好处就是对消息更容易管理。
  之所以被称为消息分流器,就是因为它可以对任何消息进行分流。下面我们做一个函数就很清楚了:
void MsgCracker(HWND hWnd,int id,HWND hWndCtl,UINT codeNotify)
{
      switch(id)
      {
     case ID_A:
                  if(codeNotify==EN_CHANGE)...
                  break;
     case ID_B:
                  if(codeNotify==BN_CLICKED)...
                  break;
             ....
       }
}
然后我们修改一下窗口过程:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
       switch(message)
      {
             HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);
             HANDLE_MSG(hWnd,WM_DESTROY,MsgCracker);
           default:
                    return DefWindowProc(hWnd, message, wParam, lParam);
   }
  return 0;
}
在WindowsX.h中定义了如下的HANDLE_MSG宏:
   #define HANDLE_MSG(hwnd,msg,fn) \
             switch(msg): return HANDLE_##msg((hwnd),(wParam),(lParam),(fn));
实际上,HANDLE_WM_XXXX都是宏,例如:HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);将被转换成如下定义:
   #define HANDLE_WM_COMMAND(hwnd,wParam,lParam,fn)\
             ((fn)((hwnd),(int)(LOWORD(wParam)),(HWND)(lParam),(UINT)HIWORD(wParam)),0L);
好了,事情到了这一步,应该一切都明朗了。
不过,我们发现在windowsx.h里面还有一个宏:FORWARD_WM_XXXX,我们还是那WM_COMMAND为例,进行分析:
   #define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) \
     (void)(fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))
所以实际上,FORWARD_WM_XXXX将消息参数进行了重新构造,生成了wParam && lParam,然后调用了我们定义的函数。
好了,事情到这里也算是也段落了,下次我们在分析消息在MFC中的处理。

posted @ 2009-02-21 08:37 wrh 阅读(404) | 评论 (0)编辑 收藏
仅列出标题
共25页: First 13 14 15 16 17 18 19 20 21 Last 

导航

<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

统计

常用链接

留言簿(19)

随笔档案

文章档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜