星期五, 十月 06, 2006

JPEG格式

JPEG格式
6.3.1 简介
微处理机中的存放顺序有正序(big endian)和逆序(little endian)之分。正序存放就是
高字节存放在前低字节在后,而逆序存放就是低字节在前高字节在后。例如,十六进制
数为A02B,正序存放就是A02B,逆序存放就是2BA0。摩托罗拉(Motorola)公司的微处理
器使用正序存放,而英特尔(Intel)公司的微处理器使用逆序。JPEG文件中的字节是按照
正序排列的。
----------------------------------------------------------------------------
----
JPEG委员会在制定JPEG标准时,定义了许多标记(marker)用来区分和识别图像数据及其
相关信息,但笔者没有找到JPEG委员会对JPEG文件交换格式的明确定义。直到1998年12
月从分析网上具体的JPG图像来看,使用比较广泛的还是JPEG文件交换格式(JPEG File
Interchange Format,JFIF)版本号为1.02。这是1992年9月由在C-Cube Microsystems公
司工作的Eric Hamilton提出的。此外还有TIFF JPEG等格式,但由于这种格式比较复杂
,因此大多数应用程序都支持JFIF文件交换格式。
JPEG文件使用的颜色空间是CCIR 601推荐标准进行的彩色空间(参看第7章)。在这个彩色
空间中,每个分量、每个像素的电平规定为255级,用8位代码表示。从RGB转换成YCbCr
空间时,使用下面的精确的转换关系:
       Y = 256 * Ey
      Cb = 256 * [ECb] + 128
      Cr = 256 * [ECr] + 128
其中亮度电平Ey和色差电平ECb和ECb分别是CCIR 601定义的参数。由于Ey的范围是
0~1,ECb和ECb的范围是-0.5~+0.5,因此Y, Cb和Cr的最大值必须要箝到255。于是
RGB和YCbCr之间的转换关系需要按照下面的方法计算。
(1) 从RGB转换成YCbCr
YCbCr(256级)分量可直接从用8位表示的RGB分量计算得到:
       Y =   0.299 R + 0.587 G  + 0.114 B
     Cb = - 0.1687R - 0.3313G  + 0.5   B + 128
    Cr = 0.5 R - 0.4187G - 0.0813 B + 128
需要注意的是不是所有图像文件格式都按照R0,G0,B0,…… Rn,Gn,Bn的次序存储样
本数据,因此在RGB文件转换成JFIF文件时需要首先验证RGB的次序。
(2) 从YCbCr转换成RGB
RGB分量可直接从YCbCr(256级)分量计算得到:
     R = Y                 + 1.402 (Cr-128)
      G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
      B = Y + 1.772 (Cb-128)
在JFIF文件格式中,图像样本的存放顺序是从左到右和从上到下。这就是说JFIF文件中
的第一个图像样本是图像左上角的样本。
6.3.2 文件结构
JFIF文件格式直接使用JPEG标准为应用程序定义的许多标记,因此JFIF格式成了事实上
JPEG文件交换格式标准。JPEG的每个标记都是由2个字节组成,其前一个字节是固定值0
xFF。每个标记之前还可以添加数目不限的0xFF填充字节(fill byte)。下面是其中的8个
标记:
SOI  0xD8            图像开始
APP0 0xE0            JFIF应用数据块
APPn 0xE1 - 0xEF    其他的应用数据块(n, 1~15)
DQT  0xDB           量化表
SOF0 0xC0            帧开始
DHT  0xC4           霍夫曼(Huffman)表
SOS  0xDA           扫描线开始
EOI  0xD9            图像结束
为使读者对JPEG定义的标记一目了然,现将JPEG的标记码列于表6-05,并保留英文解释

表6-05 JPEG定义的标记
Symbol
(符号)
Code Assignment
(标记代码)
Description
(说明)
Start Of Frame markers, non-hierarchical Huffman coding
SOF0
0xFFC0
Baseline DCT
SOF1
0xFFC1
Extended sequential DCT
SOF2
0xFFC2
Progressive DCT
SOF3
0xFFC3
Spatial (sequential) lossless
Start Of Frame markers, hierarchical Huffman coding
SOF5
0xFFC5
Differential sequential DCT
SOF6
0xFFC6
Differential progressive DCT
SOF7
0xFFC7
Differential spatial lossless
Start Of Frame markers, non-hierarchical arithmetic coding
JPG
0xFFC8
Reserved for JPEG extensions
SOF9
0xFFC9
Extended sequential DCT
SOF10
0xFFCA
Progressive DCT
SOF11
0xFFCB
Spatial (sequential) Lossless
Start Of Frame markers, hierarchical arithmetic coding
SOF13
0xFFCD
Differential sequential DCT
SOF14
0xFFCE
Differential progressive DCT
SOF15
0xFFCF
Differential spatial Lossless
Huffman table specification
DHT
0xFFC4
Define Huffman table(s)
arithmetic coding conditioning specification
DAC
0xFFCC
Define arithmetic conditioning table
Restart interval termination
RSTm
0xFFD0~0xFFD7
Restart with modulo 8 counter m
Other marker
SOI
0xFFD8
Start of image
EOI
0xFFD9
End of image
SOS
0xFFDA
Start of scan
DQT
0xFFDB
Define quantization table(s)
DNL
0xFFDC
Define number of lines
DRI
0xFFDD
Define restart interval
DHP
0xFFDE
Define hierarchical progression
EXP
0xFFDF
Expand reference image(s)
APPn
0xFFE0~0xFFEF
Reserved for application use
JPGn
0xFFF0~0xFFFD
Reserved for JPEG extension
COM
0xFFFE
Comment
Reserved markers
TEM
0xFF01
For temporary use in arithmetic coding
RES
0xFF02~0xFFBF
Reserved
JPEG文件由下面的8个部分组成:
(1) 图像开始SOI(Start of Image)标记
(2) APP0标记(Marker)
① APP0长度(length)
② 标识符(identifier)
③ 版本号(version)
④ X和Y的密度单位(units=0:无单位;units=1:点数/英寸;units=2:点数/厘米)
⑤ X方向像素密度(X density)
⑥ Y方向像素密度(Y density)
⑦ 缩略图水平像素数目(thumbnail horizontal pixels)
⑧ 缩略图垂直像素数目(thumbnail vertical pixels)
⑨ 缩略图RGB位图(thumbnail RGB bitmap)
(3) APPn标记(Markers),其中n=1~15(任选)
① APPn长度(length)
② 由于详细信息(application specific information)
(4) 一个或者多个量化表DQT(difine quantization table)
① 量化表长度(quantization table length)
② 量化表数目(quantization table number)
③ 量化表(quantization table)
(5) 帧图像开始SOF0(Start of Frame)
① 帧开始长度(start of frame length)
② 精度(precision),每个颜色分量每个像素的位数(bits per pixel per color comp
onent)
③ 图像高度(image height)
④ 图像宽度(image width)
⑤ 颜色分量数(number of color components)
⑥ 对每个颜色分量(for each component)
ID
垂直方向的样本因子(vertical sample factor)
水平方向的样本因子(horizontal sample factor)
量化表号(quantization table#)
(6) 一个或者多个霍夫曼表DHT(Difine Huffman Table)
① 霍夫曼表的长度(Huffman table length)
② 类型、AC或者DC(Type, AC or DC)
③ 索引(Index)
④ 位表(bits table)
⑤ 值表(value table)
(7) 扫描开始SOS(Start of Scan)
① 扫描开始长度(start of scan length)
② 颜色分量数(number of color components)
③ 每个颜色分量
ID
交流系数表号(AC table #)
直流系数表号(DC table #)
④ 压缩图像数据(compressed image data)
(8) 图像结束EOI(End of Image)
表6-06表示了APP0域的详细结构。有兴趣的读者可通过UltraEdit或者PC TOOLS等工具软
件打开一个JPG图像文件,对APP0的结构进行分析和验证。
表6-06 JFIF格式中APP0域的详细结构
偏移
长度
内容
块的名称
说明
0
2 byte
0xFFD8
(Start of Image,SOI)
图像开始
2
2 byte
0xFFE0
APP0(JFIF application segment)
JFIF应用数据块
4
2 bytes
  length of APP0 block
APP0块的长度
6
5 bytes
  "JFIF"+"0"
识别APP0标记
11
1 byte
  <Major version>
主要版本号(如版本1.02中的1)
12
1 byte
  <Minor version>
次要版本号(如版本1.02中的02)
13
1 byte
  <Units for the X
and Y densities>
X和Y的密度单位
units=0:无单位
units=1:点数/英寸
units=2:点数/厘米
14
2 bytes
  <Xdensity>
水平方向像素密度
16
2 bytes
  <Ydensity>
垂直方向像素密度
18
1 byte
  <Xthumbnail>
缩略图水平像素数目
19
1 byte
  <Ythumbnail>
缩略图垂直像素数目
  3n
  < Thumbnail RGB bitmap>
缩略RGB位图(n为缩略图的像素数)
      Optional JFIF extension APP0 marker segment(s)
任选的JFIF扩展APP0标记段
  ……
  ……
 
  2 byte
0xFFD9
(EOI) end-of-file
图像文件结束标记

星期三, 九月 20, 2006

图片渐渐显示(遂点谈入)

第一步:把如下代码加入<body>区域中
<SCRIPT FOR=window EVENT=onLoad LANGUAGE=vbscript>
image1.filters.item(0).apply()
image1.filters.item(0).transition = 12
image1.Style.visibility = ""
image1.filters(0).play(2.0)
</SCRIPT>


第二步:把如下代码加入<body>区域中
<img src="swimming.gif" border=5 id="image1"
style="visibility:hidden; FILTER:revealTrans(Duration=4.0, Transition=23);">

实现图片的无缝循环滚动

解决思路:
一个设定宽度并且隐藏超出它宽度的内容的容器demo,里面放demo1和demo2,demo1是滚动内容,demo2为demo1的直接克隆副本,通过不断改变demo1的scrollTop或者scrollLeft达到滚动的目的,当滚动至demo1与demo2的交界处时直接跳回初始位置,因为demo1与demo2一样,所以分不出跳动的瞬间,从而达到"无缝"滚动的目的。

具体步骤:
1.向上滚动。

<base href="http://www.yezigu.com">
<div id=demo style=overflow:hidden;height:100;width:90;
background:#214984;color:#ffffff>
<div id=demo1>
<img src="images/flash8.gif">
<img src="images/link/flashempire.gif">
<img src="images/linklogo/shlogo.gif">
<img src="images/link/deskcity.gif">
<img src="images/linklogo/5dmeng.gif">
<img src="/Upload/2005-09/logo.gif">
<img src="images/link/flashskylogo.gif">
<img src="images/link/5dlogo88.gif">
<img src="/Upload/2005-09/link.gif">
</div>
<div id=demo2></div>
</div>
<script>
var speed=30
demo2.innerHTML=demo1.innerHTML //克隆demo1为demo2
function Marquee(){
//当滚动至demo1与demo2交界时
if(demo2.offsetTop-demo.scrollTop<=0)
demo.scrollTop-=demo1.offsetHeight //demo跳到最顶端
else{
demo.scrollTop++
}
}
var MyMar=setInterval(Marquee,speed)//设置定时器
//鼠标移上时清除定时器达到滚动停止的目的
demo.onmouseover=function() {clearInterval(MyMar)}
//鼠标移开时重设定时器
demo.onmouseout=function(){MyMar=setInterval(Marquee,speed)}
</script>

2.向下滚动。

<base href="http://www.yezigu.com">
<div id=demo style=overflow:hidden;height:100;width:90;
background:#214984;color:#ffffff>
<div id=demo1>
<img src="images/flash8.gif">
<img src="images/link/flashempire.gif">
<img src="images/linklogo/shlogo.gif">
<img src="images/link/deskcity.gif">
<img src="images/linklogo/5dmeng.gif">
<img src="/Upload/2005-09/logo.gif">
<img src="images/link/flashskylogo.gif">
<img src="images/link/5dlogo88.gif">
<img src="/Upload/2005-09/link.gif">
</div>
<div id=demo2></div>
</div>
<script>
var speed=30
demo2.innerHTML=demo1.innerHTML
demo.scrollTop=demo.scrollHeight
function Marquee(){
if(demo1.offsetTop-demo.scrollTop>=0)
demo.scrollTop+=demo2.offsetHeight
else{
demo.scrollTop--
}
}
var MyMar=setInterval(Marquee,speed)
demo.onmouseover=function() {clearInterval(MyMar)}
demo.onmouseout=function() {MyMar=setInterval(Marquee,speed)}
</script>

3.向左滚动。

<base href="http://www.yezigu.com">
<div id=demo style=overflow:hidden;height:33;width:500;
background:#214984;color:#ffffff>
<table align=left cellpadding=0 cellspace=0 border=0>
<tr>
<td id=demo1 valign=top>
<img src="images/link/flashempire.gif">
<img src="images/linklogo/shlogo.gif">
<img src="images/link/deskcity.gif">
<img src="images/linklogo/5dmeng.gif">
<img src="/Upload/2005-09/logo.gif">
<img src="images/link/flashskylogo.gif">
<img src="images/link/5dlogo88.gif">
<img src="/Upload/2005-09/link.gif">
<img src="images/flash8.gif">
</td>
<td id=demo2 valign=top></td>
</tr>
</table>
</div>
<script>
var speed=30
demo2.innerHTML=demo1.innerHTML
function Marquee(){
if(demo2.offsetWidth-demo.scrollLeft<=0)
demo.scrollLeft-=demo1.offsetWidth
else{
demo.scrollLeft++
}
}
var MyMar=setInterval(Marquee,speed)
demo.onmouseover=function() {clearInterval(MyMar)}
demo.onmouseout=function() {MyMar=setInterval(Marquee,speed)}
</script>

4.向右滚动。

<base href="http://www.yezigu.com">
<div id=demo style=overflow:hidden;height:33;width:500;
background:#214984;color:#ffffff>
<table align=left cellpadding=0 cellspace=0 border=0>
<tr>
<td id=demo1 valign=top>
<img src="images/link/flashempire.gif">
<img src="images/linklogo/shlogo.gif">
<img src="images/link/deskcity.gif">
<img src="images/linklogo/5dmeng.gif">
<img src="/Upload/2005-09/logo.gif">
<img src="images/link/flashskylogo.gif">
<img src="images/link/5dlogo88.gif">
<img src="/Upload/2005-09/link.gif">
<img src="images/flash8.gif">
</td>
<td id=demo2 valign=top></td>
</tr>
</table>
</div>
<script>
var speed=30
demo2.innerHTML=demo1.innerHTML
demo.scrollLeft=demo.scrollWidth
function Marquee(){
if(demo.scrollLeft<=0)
demo.scrollLeft+=demo2.offsetWidth
else{
demo.scrollLeft--
}
}
var MyMar=setInterval(Marquee,speed)
demo.onmouseover=function() {clearInterval(MyMar)}
demo.onmouseout=function() {MyMar=setInterval(Marquee,speed)}
</script>

提示:关于循环滚动的原理,光看本例代码的话可能会觉得枯涩难懂,那样的话可以运行下面修改过的代码,注意滚动条和背景颜色的变化:

<base href="http://www.yezigu.com">
<div id=demo style="overflow-x:hidden;overflow-y:scroll;
height:100;width:90;background:#214984">
<img src="images/flash8.gif">
<img src="images/link/flashempire.gif">
<img src="images/linklogo/shlogo.gif">
<img src="images/link/deskcity.gif">
<img src="images/linklogo/5dmeng.gif">
<img src="/Upload/2005-09/logo.gif">
<img src="images/link/flashskylogo.gif">
<img src="images/link/5dlogo88.gif">
</div>
<script>
function Marquee(obj,speed){
with(obj){
innerHTML=["<div>","</div><div style=’background:green’>","</div>"].join(innerHTML)
onmouseover=function(){clearInterval(MyMar)}
onmouseout=function(){
MyMar=setInterval(function(){
with(obj)
scrollTop+=(scrollTop<=lastChild.offsetTop?1:-lastChild.offsetHeight)},speed)}
fireEvent("onmouseout")
}
}
Marquee(demo,30)
</script>

星期四, 九月 14, 2006

親ウィンドウの中央にダイログを表示

CenterWindow(); //親ウィンドウの中央にダイログを表示

星期五, 九月 08, 2006

MFC窗口中各控件的上下层次的设置


BOOL SetWindowPos(
const CWnd* pWndInsertAfter,
int x,
int y,
int cx,
int cy,
UINT nFlags
);

  • wndBottom

  • ウィンドウを Z オーダーの一番下に置きます。このCWnd が最上位のウィンドウの場合、
    ウィンドウの最上位ステータスは失われます。
    システムでは、このウィンドウをほかのすべてのウィンドウよりも下に置きます。

    将窗口(控件也是窗口)置于Z坐标的最下层

  • wndTop

  • ウィンドウを Z オーダーの先頭に置きます。

    将窗口(控件也是窗口)置于Z坐标的最上层

  • wndTopMost

  • ウィンドウを最上位でないすべてのウィンドウの上に置きます。ウィンドウは、非アクティブになったときも、最上位の位置を保持します。

    始终最上层

  • wndNoTopMost

  • ウィンドウを最上位でないすべてのウィンドウの先頭に再配置します (つまり、一番手前のすべてのウィンドウの後ろに置きます)。
    ウィンドウが既に非最上位ウィンドウとなっているときは、このフラグは無効です。



    BOOL ModifyStyle(
    DWORD dwRemove,
    DWORD dwAdd,
    UINT nFlags = 0
    );


    窗口中各控件的描画顺序
    top的最先描画
    bottom的最后描画
    导致控件的消息响应和显示矛盾

    可以通过以下方法的设置解决
    GetDlgItem(IDC_SCREEN_SWITCH_BACK)->SetWindowPos(&wndBottom, 0, 0, 50, 592, SWP_SHOWWINDOW);
    GetDlgItem(IDC_SCREEN_SWITCH_BACK)->ModifyStyle(0, WS_CLIPSIBLINGS, 0);

    星期三, 九月 06, 2006

    为OpenFile通用对话框增添预展功能

    为OpenFile通用对话框增添预展功能
      用户使用Window应用程序时,经常要打开文件,可有时用户打开文件时却打开了一个不是要求的文件,这显得非常麻烦。因此,许多应用程序便给OpenFile通用对话框增添了预展功能,使得用户在选择文件时,可以先预视其内容。在这里,笔者也向你介绍这种功能的编程方法。  WINDOW的通用对话框OpenFile,在使用,可以安装用户定义的勾子函数。这样,原OpenFile窗口的消息先通过用户的勾子函数过滤。若用户在通用对话框中增加一个Edit控制(此处,以预展文本文件内容为例),处理OpenFile通用对话框中的 CDN_SELCHANGE消息(文件名选择更改消息),即可完成这种功能。  用户定义的勾子函数,WINDOW要求其原形定义如下:  typedef UINT (APIENTRY *LPCCHOOKPROC) (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);  对于OpenFile通用对话框中的一些常见消息,WINDOWS会向用户定义的勾子发送WM_NOTIFY消息,此时,lParam指向一数据结构如下的指针:  typedef struct _OFNOTIFYA
      { NMHDR hdr;
      LPOPENFILENAMEA lpOFN;
      LPSTR pszFile; // May be NULL
      } OFNOTIFYA, FAR *LPOFNOTIFYA;
      而NMHDR在WINDOWS定义如下:  typedef struct tagNMHDR {
      HWND hwndFrom;
      UINT idFrom;
      UINT code;
      } NMHDR;
      其中:hwndFrom 表示发送消息控件的句柄;idFrom表示控件的ID值,而且code表示通告的消息。  在WINDOWS95中通告的消息在commdlg.h中定义如下  #define CDN_SELCHANGE (CDN_FIRST-0x0001) //文件名列表中的文件名更改时发送的消息
      #define CDN_FOLDERCHANGE (CDN_FIRST - 0x0002) //路径更改时发送的消息
      #define CDN_SHAREVIOLATION (CDN_FIRST - 0x0003) //共享按钮改更时发送的消息
      #define CDN_HELP (CDN_FIRST - 0x0004) //HELP按钮按下消息
      #define CDN_FILEOK (CDN_FIRST - 0x0005) //OK按钮按下消息
      因为此处,我们仅处理用户选择的文件名发生更改时,才预展,因此我们定义的勾子函数中,仅处理CDN_SELCHANGE消息。  另外,当什么文件都预展时,用户会发现WINDOWS的速度明显减慢,因此,我决定在OpenFile对话框中增加一个"是否预展"的CheckBox按钮。只有当用户选中了它时才预展。当然,此时,还需要我们自己处理这个CheckBox发送的消息。  以上问题解决后,我们只需要将一个Edit和一个CheckBox增加到一个无窗体的对话框中,并将WINDOWS本身的OpenFile对话框用一个TEXT控件代替,ID值设置为stc32(定义在dlgs.h中)。  此时还需要将OPENFILENAME结构中的lpfnHook成员,设置成用户定义的勾子函数,将lpTemplateName成员设置成用户定义对话框的ID值(需要用MAKEINTRESOURCE),此外,成员Flags需要设置成OFN_EXPLORER(WINDOW32中使用) OFN_ENABLEHOOK(允许使用用户定义的勾子函数) OFN_ENABLETEMPLATE(允许用户定义的对话框模板)。  现在一切就绪,剩下的工作便是着手编写这一程序,看看这动人的一幕。  以下为我设计的全部源程序。此程序在PWIN95,Borland C++5.0中通过。 //OpenFile.rh
    #ifndef _OPENFILE_RH
    #define _OPENFILE_RH
    #define IDD_OPENFILE 100 //打开文件
    #define IDM_FILEOPEN 106 //打开文件菜单命令
    #define IDM_EXIT 107 //退出
    #define IDE_EDIT 1022 //预展EDIT
    #define ID_PRESHOW 1023 //预展CheckBox按钮
    #endif
    //OpenFile.h
    #define STRICT
    #ifdef RC_INVOKED
    #include
    #else
    #include
    #endif
    #define MAX_PATH 260
    long APIENTRY MainWndProc(HWND, UINT, UINT, LONG);
    BOOL CALLBACK OpenFileNameDlgProc(HWND ,UINT,UINT,LONG);
    BOOL InitApplication(HANDLE);
    BOOL InitInstance(HANDLE, int);
    void CALLBACK SetErrorMsg(HWND,UINT,const char *,char *);
    BOOL OpenTheFile( OPENFILENAME *);
    BOOL NEAR PASCAL TestNotify(HWND, LPOFNOTIFY);
    //OpenFile.RC
    #include "openfile.rh"
    #include

    IDD_OPENFILE DIALOG 0, 0, 251, 130
    STYLE DS_3DLOOK DS_CONTROL WS_CHILD WS_VISIBLE WS_CLIPSIBLINGS
    FONT 9, "MS Sans Serif"
    {
    LTEXT "", stc32, 4, 4, 124, 125
    CONTROL "以文本方式预展", ID_PRESHOW, "button", BS_AUTOCHECKBOX WS_CHILD
    WS_VISIBLE WS_TABSTOP, 132, 121, 104, 9
    CONTROL "", IDE_EDIT, "edit", ES_LEFT ES_MULTILINE ES_AUTOVSCROLL
    ES_AUTOHSCROLL WS_CHILD WS_VISIBLE WS_BORDER WS_TABSTOP,
    132, 4, 116, 112
    }
    OPENFILEMENU MENU
    {
    POPUP "&Options"
    {
    MENUITEM "&File Open...", IDM_FILEOPEN
    MENUITEM "E&xit", IDM_EXIT
    }
    }
    //OpenFile.h
    #include
    #include
    #include
    #include "openfile.rh"
    #include "openfile.h"
    HINSTANCE g_hInst; //应用程序的实例句柄
    OPENFILENAME OpenFileName; //打开文件结构
    char szFile[MAX_PATH]; //文件名
    #pragma argsused
    int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow )
    { MSG msg;
    OSVERSIONINFO osVer;
    osVer.dwOSVersionInfoSize = sizeof(osVer);
    if (!GetVersionEx(&osVer)) //取版本信息不成功返回
    return (FALSE);
    if (osVer.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
    {//不在Window95环境下
    MessageBox(NULL, "此程序需要在Windows 95下运行.", "警告", MB_OK );
    return (FALSE);
    }
    if (!InitApplication(hInstance)) //初始化应用程序失败
    return (FALSE);
    if (!InitInstance(hInstance, nCmdShow))
    return (FALSE);
    while (GetMessage(&msg,NULL,0,0))
    { TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    return (msg.wParam);
    }
    BOOL InitApplication(HANDLE hInstance)
    { WNDCLASS wc;
    wc.style = 0;
    wc.lpfnWndProc = (WNDPROC)MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = "OpenFileMenu";
    wc.lpszClassName = "OpenFileNameClass";
    return (RegisterClass(&wc));
    }
    BOOL InitInstance( HANDLE hInstance, int nCmdShow)
    { HWND hWnd;
    g_hInst = hInstance; //保存当前应用程序的实例句柄
    hWnd = CreateWindow(
    "OpenFileNameClass",
    "为你的OpenFile增加预视功能",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    NULL,
    NULL,
    g_hInst,
    NULL
    );
    if (!hWnd) //窗口不能建立
    return (FALSE);
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return (TRUE);
    }
    LONG APIENTRY MainWndProc( HWND hWnd, UINT message, UINT wParam, LONG lParam)
    { static HWND hwndEdit;
    CHAR lpszHello[] = "请从菜单上选择Open File,可看到OpenFile中的预视功能.";
    switch (message) {
    case WM_CREATE:
    OpenFileName.lStructSize = sizeof(OPENFILENAME); //结构大小
    OpenFileName.hwndOwner = hWnd;
    OpenFileName.hInstance = g_hInst;
    OpenFileName.lpstrFilter= "文本文件名(*.txt)\0*.TXT\0所有文件(*.*)\0*.*\0\0";
    OpenFileName.lpstrCustomFilter =NULL;
    OpenFileName.nMaxCustFilter = 0;
    OpenFileName.nFilterIndex = 0;
    OpenFileName.lpstrFile = szFile;
    OpenFileName.nMaxFile = sizeof(szFile);
    OpenFileName.lpstrFileTitle = NULL;
    OpenFileName.nMaxFileTitle = 0;
    OpenFileName.lpstrInitialDir = NULL;
    OpenFileName.lpstrTitle = "打开文件"; //窗口标题
    OpenFileName.nFileOffset = 0;
    OpenFileName.nFileExtension = 0;
    OpenFileName.lpstrDefExt = "txt"; //扩展文件名
    hwndEdit = CreateWindow( //建立一编辑窗口
    "EDIT", NULL,
    WS_CHILD WS_VISIBLE WS_VSCROLL
    ES_LEFT ES_MULTILINE ES_AUTOVSCROLL,
    0, 0, 0, 0,
    hWnd,
    NULL,
    (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE),
    NULL);
    //设置信息
    SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM) lpszHello);
    return 0;
    case WM_SIZE:
    MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
    return 0;
    case WM_COMMAND:
    switch( LOWORD( wParam ))
    { case IDM_FILEOPEN: //选中了打开文件菜单项
    if(OpenTheFile( &OpenFileName))
    SendMessage(hwndEdit, WM_SETTEXT, 0,
    (LPARAM)OpenFileName.lpstrFile);
    break;
    case IDM_EXIT:
    PostQuitMessage(0);
    break;
    default:
    return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    break;
    case WM_DESTROY:
    PostQuitMessage(0);
    break;
    default:
    return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (0);
    }
    //函数作用:打开文件
    BOOL OpenTheFile(OPENFILENAME *OpenFileName)
    { OpenFileName->lCustData = NULL;
    OpenFileName->lpfnHook = (LPOFNHOOKPROC) OpenFileNameDlgProc;
    OpenFileName->lpTemplateName = MAKEINTRESOURCE(IDD_OPENFILE);
    OpenFileName->Flags =OFN_EXPLORER OFN_HIDEREADONLY
    OFN_ENABLEHOOK OFN_ENABLETEMPLATE;
    return(GetOpenFileName(OpenFileName));
    }
    BOOL NEAR PASCAL TestNotify(HWND hDlg, LPOFNOTIFY pofn)
    { HFILE hFile; //打开文件名柄
    long dwBytesRead; long dwFileSize;
    LPSTR lpBuf; char szFile[MAX_PATH];
    if (pofn->hdr.code!=CDN_SELCHANGE) //文件选择改变
    return(FALSE);
    if (SendDlgItemMessage(hDlg,ID_PRESHOW,BM_GETCHECK,0,0L)==0)
    //用户没有选中预展按钮,则直接返回
    return(FALSE);
    if (CommDlg_OpenSave_GetSpec(GetParent(hDlg), //获取选中的文件名
    szFile, sizeof(szFile)) <= sizeof(szFile))
    { hFile =_lopen((LPSTR)szFile,OF_READ);
    if (hFile==HFILE_ERROR)
    {SetErrorMsg(hDlg,IDE_EDIT,"文件[%s]不能开",szFile);
    return FALSE; //打开文件失败失败,返回
    }
    dwFileSize =_llseek(hFile,0L,FILE_END); //取文件大小
    if (dwFileSize == HFILE_ERROR) //失败,则返回
    { SetErrorMsg(hDlg,IDE_EDIT,"移动文件[%s]指针出错.",szFile);
    _lclose(hFile); return FALSE;
    }
    _llseek(hFile,0L,FILE_BEGIN);
    lpBuf = (char *)GlobalAlloc( GMEM_FIXED, dwFileSize );
    if (lpBuf == NULL ) //分配内存失败失败
    {SetErrorMsg(hDlg,IDE_EDIT,"不能为文件[%s],分配内存",szFile);
    _lclose( hFile );
    return FALSE;
    }
    dwBytesRead= _lread(hFile,(LPVOID)lpBuf, dwFileSize);
    //读文件到lpBUf处
    if (dwBytesRead ==HFILE_ERROR) //读文件失败
    { SetErrorMsg(hDlg,IDE_EDIT,"读文件[%s]失败",szFile);
    GlobalFree(lpBuf);
    _lclose( hFile );
    return FALSE;
    }
    SetDlgItemText(hDlg,IDE_EDIT,lpBuf);
    _lclose(hFile); //关闭文件
    GlobalFree(lpBuf);//释放分配的内存
    return TRUE;
    }
    else return(TRUE);
    }
    #pragma argsused
    void CALLBACK SetErrorMsg(HWND hDlg,UINT id,const char *format,char *msg)
    { char buf[MAX_PATH];
    wsprintf(buf,format,msg);
    SetDlgItemText(hDlg,id,buf);
    }
    #pragma argsused
    BOOL CALLBACK OpenFileNameDlgProc(HWND hDlg, UINT uMsg,
    WPARAM wParam,
    LPARAM lParam)
    { char buf=0;
    OFNOTIFY of;
    switch (uMsg)
    { case WM_COMMAND: //用户在PreShow按钮上改变了显示
    if(wParam==ID_PRESHOW)
    { if (!SendDlgItemMessage(hDlg,ID_PRESHOW,BM_GETCHECK,0,0L))
    //没有选中,则将预展EDIT窗口设置为空
    SetDlgItemText(hDlg,IDE_EDIT,&buf);
    else
    {of.hdr.code=CDN_SELCHANGE; //否则,调用TestNotify处理
    TestNotify(hDlg,(LPOFNOTIFY)&of);
    }
    }
    return FALSE;
    case WM_NOTIFY:
     TestNotify(hDlg, (LPOFNOTIFY)lParam);
     return(TRUE);
    default:
    return FALSE;
    }
    }

    通过文件路径获取文件名和扩展名 _tsplitpath

    _tsplitpath
    通过文件路径获取文件名和扩展名
    // crt_makepath.c
    #include
    #include
    int main( void )
    {
    char path_buffer[_MAX_PATH];
    char drive[_MAX_DRIVE];
    char dir[_MAX_DIR];
    char fname[_MAX_FNAME];
    char ext[_MAX_EXT];
    _makepath( path_buffer, "c", "file:////sample//crt//", "makepath", "c" );
    printf( "Path created with _makepath: %s\n\n", path_buffer );
    _splitpath( path_buffer, drive, dir, fname, ext );
    printf( "Path extracted with _splitpath:\n" );
    printf( " Drive: %s\n", drive );
    printf( " Dir: %s\n", dir );
    printf( " Filename: %s\n", fname );
    printf( " Ext: %s\n", ext );
    }
    パス名を構成要素に分解します。
    void _splitpath(const char *path,char *drive,char *dir,
    char *fname,char *ext );
    void _wsplitpath(const wchar_t *path,wchar_t *drive,
    wchar_t *dir,wchar_t *fname,wchar_t *ext );
    パラメータ
    path
    完全パス。
    drive
    ドライブを表す文字 (省略可能)。後ろにコロン (:) を付けます。
    dir
    ディレクトリ パスと末尾のスラッシュ (省略可能)。後ろにスラッシュ (/)、円記号 (\) を付けます (混在可)。
    fname
    ベース ファイル名 (拡張子なし)。
    ext
    ファイル名の拡張子 (省略可能)。先頭にピリオド (.) が付きます。
    解説-〉详见msdn

    星期四, 八月 31, 2006

    CListCtrl(2)

    实现点击列头排序:
    定义可以排序的列表类
    只需要多定义两个变量
    class SortCListCtrl : public CListCtrl
    {
    // Construction
    public:
    SortCListCtrl();
    // Attributes
    public:
    BOOL m_fAsc;//是否顺序排序
    int m_nSortedCol;//当前排序的列
    ....
    }
    在使用可以排序列表时 实例化自己的变量
    SortCListCtrl m_yktlist;
    //响应点击列函数
    void CAuditingCertView::

    OnColumnclickListYkt(NMHDR* pNMHDR, LRESULT* pResult)
    {
    for (int i = 0; i < m_yktlist.GetItemCount(); i)
    {
    m_yktlist.SetItemData(i, i);//供排序使用的item编号
    }
    NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
    //设置排序方式
    if( pNMListView->iSubItem ==m_yktlist.m_nSortedCol )
    m_yktlist.m_fAsc = !m_yktlist.m_fAsc;
    else
    {
    m_yktlist.m_fAsc = TRUE;
    m_yktlist.m_nSortedCol = pNMListView->iSubItem;
    }
    //调用排序函数,此函数为CListCtrl定义好的,但是需要调

    用我们定义的函数才比较任意两个项目的值
    m_yktlist.SortItems(MyListCompare, (LPARAM)&m_yktlist);
    for ( i = 0; i < m_yktlist.GetItemCount(); i){
    m_yktlist.SetItemData(i, i);//供排序使用的item编号
    CString s;
    s.Format("%d",i 1);//编号
    m_yktlist.SetItemText(i,0,s);
    }
    *pResult = 0;
    }
    ///全局函数,比较两个项目的依据
    int CALLBACK MyListCompare(LPARAM lParam1,

    LPARAM lParam2, LPARAM lParamSort)
    {
    //通过传递的参数来得到CSortList对象指针,从而得到排序方式
    SortCListCtrl * pV=(SortCListCtrl *)lParamSort;
    //通过ItemData来确定数据
    CString szComp1,szComp2;
    int iCompRes;
    szComp1=pV->GetItemText(lParam1,pV->m_nSortedCol);
    szComp2=pV->GetItemText(lParam2,pV->m_nSortedCol);
    switch(pV->m_nSortedCol)
    {
    case(0):
    //以第一列为根据排序 编号
    iCompRes=atof(szComp1)<=atof(szComp2)?-1:1;
    break;
    case(4):
    //以第5列为根据排序 总次数
    iCompRes=atof(szComp1)<=atof(szComp2)?-1:1;
    break;
    default:
    iCompRes=szComp1.Compare(szComp2);
    break;
    }
    //根据当前的排序方式进行调整
    if(pV->m_fAsc)
    return iCompRes;
    else
    return -iCompRes;
    }
    导出数据为excel文件
    使用ODBC将数据输出到excel数据区
    void ExportAsExcel(CString filename,

    CListCtrl &resultlist,CWnd * wnd)
    {
    CDatabase database;
    CString sDriver = "MICROSOFT EXCEL DRIVER (*.XLS)";

    // Excel安装驱动
    CString sSql,sExcelFile;
    //弹出对话框选择路径
    CFileDialog fileDlg (FALSE, "Path", filename,

    OFN_FILEMUSTEXIST OFN_HIDEREADONLY, "*.xls",wnd);
    if( fileDlg.DoModal()==IDOK)
    {
    sExcelFile = fileDlg.GetPathName(); // 要建立的Excel文件
    CFileFind finder;
    BOOL bWorking = finder.FindFile(sExcelFile);//寻找文件
    if (bWorking)//如果已经存在文件,则删除
    {
    CFile::Remove((LPCTSTR)sExcelFile);
    }
    }
    else return;
    TRY
    {
    // 创建进行存取的字符串
    sSql.Format("DRIVER={%s};

    DSN='';FIRSTROWHASNAMES=1;READONLY=FALSE;
    CREATE_DB=\"%s\";DBQ=%s",sDriver, sExcelFile, sExcelFile);
    // 创建数据库 (既Excel表格文件) if( database.OpenEx(sSql,

    CDatabase::noOdbcDialog) )
    {
    CHeaderCtrl* pHeader = resultlist.GetHeaderCtrl();
    //获得行,列的个数
    int nColCount = pHeader->GetItemCount();
    int nLineCount = resultlist.GetItemCount();
    int ColOrderArray[100];
    CString ca[100];
    resultlist.GetColumnOrderArray(ColOrderArray, nColCount);
    //检索各列的信息,确定列标题的内容
    for(int i =0 ; i< nColCount; i )
    {
    LVCOLUMN lvc;
    char text[100];
    lvc.mask = LVCF_TEXTLVCF_SUBITEM;
    lvc.pszText = text;
    lvc.cchTextMax = 100;
    resultlist.GetColumn(ColOrderArray[i], &lvc);
    ca[i] = lvc.pszText;
    }
    // 创建表结构
    CString tempsql="(";
    for(i =0 ; i< nColCount-1; i )
    {
    tempsql =ca[i];
    tempsql =" TEXT,";
    }
    tempsql =ca[nColCount-1];
    tempsql =" TEXT)";
    sSql = "CREATE TABLE Sheet1 ";
    sSql =tempsql;
    database.ExecuteSQL(sSql);
    //插入数据
    int item_count=resultlist.GetItemCount();
    tempsql="(";
    for(i =0 ; i< nColCount-1; i )
    {
    tempsql =ca[i];
    tempsql =" ,";
    }
    tempsql =ca[nColCount-1];
    tempsql =")";
    for(int itemnum=0;itemnum<item_count;itemnum ){
    sSql="";
    sSql ="INSERT INTO Sheet1 ";
    sSql =tempsql;
    sSql ="VALUES ('";
    for(i =0 ; i< nColCount-1; i )
    {
    sSql =resultlist.GetItemText(itemnum, i);
    sSql ="','";
    }
    sSql =resultlist.GetItemText(itemnum, nColCount-1);
    sSql ="')";
    database.ExecuteSQL(sSql);
    }
    }
    // 关闭数据库
    database.Close();
    AfxMessageBox("Excel文件写入成功!");
    }
    CATCH_ALL(e)
    {
    TRACE1("Excel驱动没有安装: %s",sDriver);
    }
    END_CATCH_ALL;
    }

    CListCtrl 使用心得

    CListCtrl 使用心得
    初始化:
    DWORD dwStyle;
    dwStyle = m_bzlist.GetStyle();
    dwStyle = LVS_EX_GRIDLINES LVS_EX_FULLROWSELECTLVS_SHOWSELALWAYS ;
    m_bzlist.SetExtendedStyle(dwStyle);
    m_bzlist.SetBkColor(RGB(0xec,0xf1,0xfd));
    m_bzlist.SetTextBkColor(RGB(0xfe,0xFF,0xc6));

    插入一列:
    m_bzlist.InsertColumn(0,"编号");
    m_bzlist.SetColumnWidth(0,50);

    插入一行: 方法1:
    LV_ITEM lvitem; lvitem.pszText="";
    lvitem.mask=LVIF_TEXT;
    lvitem.iSubItem=0;
    lvitem.iItem=0;
    m_jbxxlist.InsertItem(&lvitem);
    m_jbxxlist.SetItemText(0,0,xh);
    m_jbxxlist.SetItemText(0,1,xm);
    m_jbxxlist.SetItemText(0,2,nj);

    方法2:
    m_yktlist.InsertItem(i,"2");
    m_yktlist.SetItemText(i,0,s);
    m_yktlist.SetItemText(i,1,xh);
    m_yktlist.SetItemText(i,2,xm);

    读取数据:
    resultlist.GetItemText(行数, 列数);

    每行前有复选框的列表:
    初始化时使用LVS_EX_CHECKBOXES属性
    DWORD dwStyle;
    dwStyle = m_yktlist.GetStyle();
    dwStyle = LVS_EX_GRIDLINES LVS_EX_FULLROWSELECTLVS_EX_CHECKBOXES ;
    m_yktlist.SetExtendedStyle(dwStyle);

    设置选中:
    m_yktlist.SetItemState (行数,LVIS_SELECTED, LVIS_SELECTED);//设为选中状态
    m_yktlist.SetCheck(行数,true/false);

    判断是否选中:
    m_yktlist.GetItemState(行数,LVIS_SELECTED)==LVIS_SELECTED//选中
    m_yktlist.GetCheck(行数)

    选中当前选中的 Item :
    POSITION pos = mListDvdInfo.GetFirstSelectedItemPosition();
    while(pos != NULL){ int nIndex = mListDvdInfo.GetNextSelectedItem(pos); } Item

    改变的消息相应:
    LVN_ITEMCHANGED LVN_ITEMCHANGING

    在ListCtrl中进行排序

    在ListCtrl中进行排序
    列表控件(CListCtrl)的顶部有一排按钮,用户可以通过选择不同的列来对记录进行排序。但是 CListCtrl并没有自动排序的功能,我们需要自己添加一个用于排序的回调函数来比较两个数据的大小,此外还需要响应排序按钮被点击的消息。下面讲述一下具体的做法。

    CListCtrl提供了用于排序的函数,函数原型为:BOOL CListCtrl::SortItems( PFNLVCOMPARE pfnCompare, DWORD dwData )。其中第一个参数为全局排序函数的地址,第二个参数为用户数据,你可以根据你的需要传递一个数据或是指针。该函数返回-1代表第一项排应在第二项前面,返回1代表第一项排应在第二项后面,返回0代表两项相等。

    用于排序的函数原形为:int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort),其中第三个参数为调用者传递的数据(即调用SortItems时的第二个参数dwData)。第一和第二个参数为用于比较的两项的ItemData,你可以通过DWORD CListCtrl::GetItemData( int nItem )/BOOL CListCtrl::SetItemData( int nItem, DWORD dwData )来对每一项的ItemData进行存取。在添加项时选用特定的CListCtrl::InsertItem也可以设置该值。由于你在排序时只能通过该值来确定项的位置所以你应该比较明确的确定该值的含义。

    最后一点,我们需要知道什么时候需要排序,实现这点可以在父窗口中对LVN_COLUMNCLICK消息进行处理来实现。

    下面我们看一个例子,这个例子是一个派生类,并支持顺序/倒序两种方式排序。为了简单我对全局数据进行排序,而在实际应用中会有多组需要排序的数据,所以需要通过传递参数的方式来告诉派序函数需要对什么数据进行排序。


    //全局数据
    struct DEMO_DATA
    {
    char szName[20];
    int iAge;
    }strAllData[5]={{"王某",30},{"张某",40},{"武某",32},{"陈某",20},{"李某",36}};

    //CListCtrl派生类定义
    class CSortList : public CListCtrl
    {
    // Construction
    public:
    CSortList();
    BOOL m_fAsc;//是否顺序排序
    int m_nSortedCol;//当前排序的列
    protected:
    //{{AFX_MSG(CSortList)
    //}}AFX_MSG
    ...
    };

    //父窗口中包含该CListCtrl派生类对象
    class CSort_in_list_ctrlDlg : public CDialog
    {
    // Construction
    public:
    CSort_in_list_ctrlDlg(CWnd* pParent = NULL); // standard constructor

    // Dialog Data
    //{{AFX_DATA(CSort_in_list_ctrlDlg)
    enum { IDD = IDD_SORT_IN_LIST_CTRL_DIALOG };
    CSortList m_listTest;
    //}}AFX_DATA
    }

    //在父窗口中定义LVN_COLUMNCLICK消息映射
    BEGIN_MESSAGE_MAP(CSort_in_list_ctrlDlg, CDialog)
    //{{AFX_MSG_MAP(CSort_in_list_ctrlDlg)
    ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, OnColumnclickList1)
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()

    //初始化数据
    BOOL CSort_in_list_ctrlDlg::OnInitDialog()
    {
    CDialog::OnInitDialog();

    //初始化ListCtrl中数据列表
    m_listTest.InsertColumn(0,"姓名");
    m_listTest.InsertColumn(1,"年龄");
    m_listTest.SetColumnWidth(0,80);
    m_listTest.SetColumnWidth(1,80);
    for(int i=0;i<5;i++)
    {
    m_listTest.InsertItem(i,strAllData[i].szName);
    char szAge[10];
    sprintf(szAge,"%d",strAllData[i].iAge);
    m_listTest.SetItemText(i,1,szAge);
    //设置每项的ItemData为数组中数据的索引
    //在排序函数中通过该ItemData来确定数据
    m_listTest.SetItemData(i,i);
    }
    return TRUE; // return TRUE unless you set the focus to a control
    }

    //处理消息
    void CSort_in_list_ctrlDlg::OnColumnclickList1(NMHDR* pNMHDR, LRESULT* pResult)
    {
    NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
    //设置排序方式
    if( pNMListView->iSubItem == m_listTest.m_nSortedCol )
    m_listTest.m_fAsc = !m_listTest.m_fAsc;
    else
    {
    m_listTest.m_fAsc = TRUE;
    m_listTest.m_nSortedCol = pNMListView->iSubItem;
    }
    //调用排序函数
    m_listTest.SortItems( ListCompare, (DWORD)&m_listTest );
    *pResult = 0;
    }

    //排序函数实现
    int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
    {
    //通过传递的参数来得到CSortList对象指针,从而得到排序方式
    CSortList* pV=(CSortList*)lParamSort;

    //通过ItemData来确定数据
    DEMO_DATA* pInfo1=strAllData+lParam1;
    DEMO_DATA* pInfo2=strAllData+lParam2;
    CString szComp1,szComp2;
    int iCompRes;
    switch(pV->m_nSortedCol)
    {
    case(0):
    //以第一列为根据排序
    szComp1=pInfo1->szName;
    szComp2=pInfo2->szName;
    iCompRes=szComp1.Compare(szComp2);
    break;
    case(1):
    //以第二列为根据排序
    if(pInfo1->iAge == pInfo2->iAge)
    iCompRes = 0;
    else
    iCompRes=(pInfo1->iAge < pInfo2->iAge)?-1:1;
    break;
    default:
    ASSERT(0);
    break;
    }
    //根据当前的排序方式进行调整
    if(pV->m_fAsc)
    return iCompRes;
    else
    return iCompRes*-1;
    }

    排序最快:
    CListCtrl::SortItems
    Example

    // Sort the item in reverse alphabetical order.
    static int CALLBACK
    MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
    {
    // lParamSort contains a pointer to the list view control.
    // The lParam of an item is just its index.
    CListCtrl* pListCtrl = (CListCtrl*) lParamSort;
    CString strItem1 = pListCtrl->GetItemText(lParam1, 0);
    CString strItem2 = pListCtrl->GetItemText(lParam2, 0);

    return strcmp(strItem2, strItem1);
    }

    void snip_CListCtrl_SortItems()
    {
    // The pointer to my list view control.
    extern CListCtrl* pmyListCtrl;

    // Sort the list view items using my callback procedure.
    pmyListCtrl->SortItems(MyCompareProc, (LPARAM) pmyListCtrl);
    }

    星期三, 八月 30, 2006

    ビットマップボタン

    WTLでは標準コントロールやコモンコントロールの他に、独自のコントロール用クラスを用意しています。

     ビットマップボタンは、ビットマップ画像からボタンの外観を作成できるボタンコントロールです。 WTLはビットマップボタンを作成するためにCBitmapButtonというクラスを用意しています。 MFCにも同名のクラスがありますが、それとは使用方法が異なります。

    CBitmapButtonクラスはCButtonクラスから派生しています。以下に示すのは、CBitmapButtonクラスを使用する例です。下の図のような白いボタンを作成します。


    // stdafx.h内 
    #include <atlbase.h> 
    #include <atlapp.h> 
    extern CAppModule _Module; 
    #include <atlwin.h>  
    #include <atlcrack.h> 
    #include <atlmisc.h> 
    #include <atlctrls.h> 
    #include <atlctrlx.h>  
    // CBitmapButtonクラスを使用するため    

    // maindlg.h内 
    class CMainDlg : public CDialogImpl<CMainDlg> 
    { 
    public:     
    enum { IDD = IDD_MAINDLG };      
    CBitmapButton m_button_bmp;     // ビットマップボタン      
    // メッセージマップ     
    BEGIN_MSG_MAP_EX(CMainDlg)         
    MSG_WM_INITDIALOG(OnInitDialog)         
    COMMAND_ID_HANDLER_EX(IDC_BUTTON_BMP, OnButtonBmp)         
    COMMAND_ID_HANDLER_EX(IDOK, OnOK)         
    COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel)     
    END_MSG_MAP()      
     
    LRESULT OnInitDialog(HWND hWnd, LPARAM lParam){         
    // スクリーンの中央に配置         
    CenterWindow();          
    // 大きいアイコン設定         
    HICON hIcon = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR,             
                                       ::GetSystemMetrics(SM_CXICON), 
                                       ::GetSystemMetrics(SM_CYICON));         
    SetIcon(hIcon, TRUE);                  
    // 小さいアイコン設定         
    HICON hIconSmall = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR,             
                                       ::GetSystemMetrics(SM_CXSMICON), 
                                       ::GetSystemMetrics(SM_CYSMICON));         
    SetIcon(hIconSmall, FALSE);          // コントロール設定         
    CImageList il;         
    il.CreateFromImage(IDB_BITMAP_BUTTON, 64, 1,             
    CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION);          
    m_button_bmp.SubclassWindow(GetDlgItem(IDC_BUTTON_BMP));         
    m_button_bmp.SetImageList(il);         
    m_button_bmp.SetImages(0, 1, 2, 3);         
    m_button_bmp.SetToolTipText(_T("ビットマップボタン"));         
    m_button_bmp.CenterWindow();          
    return TRUE;     }      
     
    void OnButtonBmp(UINT uNotifyCode, int nID, HWND hWndCtl){         
    MessageBox(_T("これはビットマップボタンです。"));     
    }      
     
    void OnOK(UINT uNotifyCode, int nID, HWND hWndCtl){         
    EndDialog(nID);     
    }      
     
    void OnCancel(UINT uNotifyCode, int nID, HWND hWndCtl){         
    EndDialog(nID);     
    } };    

    // Control.cpp内 
    #include "stdafx.h"  
    #include "resource.h"  
    #include "maindlg.h"  
    CAppModule _Module;  
    int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, 
                        LPTSTR lpCmdLine, int nCmdShow) {     
    HRESULT hRes = ::CoInitialize(NULL);     
    ATLASSERT(SUCCEEDED(hRes));      
    ::DefWindowProc(NULL, 0, 0, 0L);      
    AtlInitCommonControls(ICC_COOL_CLASSES  ICC_WIN95_CLASSES);      
    hRes = _Module.Init(NULL, hInstance);     
    ATLASSERT(SUCCEEDED(hRes));      
    int nRet = 0;     // BLOCK: アプリケーション実行     {         
    CMainDlg dlgMain;         
    nRet = dlgMain.DoModal();     }      
    _Module.Term();     
    ::CoUninitialize();      
    return nRet; }    

     まず、リソースを作成します。ダイアログにボタンコントロールを配置し、リソースIDを次のように指定します。

    コントロール名リソースID
    プッシュボタンIDC_BUTTON_BMP

    また、プロジェクトに次のようなビットマップリソースをIDB_BITMAP_BUTTONというIDで追加します。



    このビットマップは、ボタンの4つの状態(通常の状態、押された状態、フォーカスが当たっている状態、使用不可の状態)を表す一つの画像です。

     次に、stdafx.h内では、CBitmapButtonクラスを使用するためにatlctrls.hヘッダとatlctrlx.hヘッダをインクルードします。なお、atlctrls.hヘッダを先にインクルードする必要があります。

     CMainDlgクラスでは、まず、 CBitmapButtonクラスのインスタンスをメンバ変数として宣言します。これを使うためには、WM_INITDIALOGメッセージハンドラでサブクラス化する必要があります。

     次に、WM_INITDIALOGメッセージハンドラでは、ビットマップリソース(ID:IDB_BITMAP_BUTTON)からイメージリストを作成し、それをSetImageList()によってビットマップボタンに設定します。イメージリストを設定した後は、SetImages()によってイメージリストのインデックスとボタンの状態を関連付けます。 SetImages()は、第1引数から順に、通常の状態、押された状態、フォーカスが当たった状態、使用不可の状態のイメージリストのインデックスを受け取ります。さらに、ビットマップボタンには、SetToolTipText()によってツールチップを設定しています。

     最後に、リソースIDがIDC_BUTTON_BMPのコマンドメッセージハンドラとして、 OnButtonBmp()を追加します。このハンドラでは単にメッセージボックスを表示しているだけです。

     このように、MFCのビットマップボタンではボタンの状態別の4つビットマップリソースを用意しなければならないのに対し、 WTLのビットマップボタンはイメージリストからボタンの外観を作成します。

     WTLのビットマップボタンは、独自の拡張スタイルを用意しています。

    • BMPBTN_HOVER
      マウスカーソルがビットマップボタンの上を通ると、フォーカスが当たった状態の画像を表示します。

    • BMPBTN_AUTO3D_SINGLE
      画像の周りに3Dの境界線を描画します。

    • BMPBTN_AUTO3D_DOUBLE
      BMPBTN_AUTO3D_SINGLEよりも少し太い境界線を描画します。

    • BMPBTN_AUTOSIZE
      画像の大きさに合うようにビットマップボタンのサイズを自動的に変更します。このスタイルはデフォルトで設定されています。

    • BMPBTN_SHAREIMAGELISTS
      ビットマップボタンのオブジェクトが破棄されても、ビットマップボタンに設定されたイメージリストのオブジェクトは破棄しません。

    • BMPBTN_AUTOFIRE
      ビットマップボタンを押し続けると、繰り返しWM_COMMANDメッセージを発生させます。

    これらの拡張スタイルは、SetBitmapButtonExtendedStyle()によって設定します。

    CBitmapButton のツールチップ (CTalkButton クラス)

    CTalkButton クラスについて CFolderDialog に続いて
    作った2つ目のオリジナルの クラスだ。 できればこんな遠
    回りは したくなかったが、結果的に大仕事になってしまった。
    (前と同じじゃん。) 原因は VC++環境のツールバーに
    ある。大きさは変えられるものの 色が16色しか使えない。
    いまどきこんな色数じゃボタンの機能を表すにあたって著
    しく 表現の幅を狭められてしまう。  アイコンも最初は16
    色になっているので酩酊してしまうが、後で256色に変更
    できる ので、 まあよしとしよう。(最初から256色だとな
    およいが。 自作のアプリケーションで 16色アイコンなん
    ぞ使ったことがない。)せっかくツールバー 用にと画像を用
    意したのに16色に落とすと何がなんだか分からなくなって
    しまった。 努力を無にしたくなかったのでビットマップボタン
    を並べてツールバーに 見せたが、 ツールチップが出ない
    ところや、 ステータスバーの表示もないところがいかにも
    自作という感じだ。 で、自作のツールバーにツールチップ
    ステータスバーの表示機能を持たせようという試みが
    泥沼化しこのクラスになった。 本当は CDialogBar を
    使いたかったがダイアログエディタで乗っけた CButton
    に BS_ BITMAP でもってビットマップを貼り付けるのがど
    うしてもうまくいかず、そのうちに CTalkButtonクラスの
    方が何とか形になったので途中であきらめてしまった。
    CButton の BS_BITMAP を使いこなす方法はだい
    ぶ後になってら分かった。ついでにツールチップのテキス
    トの入れ も。でも CReBar が使いこなせた方がプロっぽ
    いかも?)class CTalkButton CBitmapButton をその
    まま使ってたんではどうしてもツールチップやステータスバー
    の表 示が うまくできなかった。なのでこのクラスは
    CBitmapButton を継承し機能拡張を行っている。
    #if !defined(AFX_TALKBUTTON_H__1DC177F6
    _3055_45D1_9E99_0B07854838C4__INCLUDED_)

    #define AFX_TALKBUTTON_H__1DC177F6_

    3055_45D1_9E99_0B07854838C4__INCLUDED_

    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    // TalkButton.h : ヘッダー ファイル
    //
    ///////////////////////////////////////////////////
    // CTalkButton ウィンドウ
    class CTalkButton : public CBitmapButton
    {
    // コンストラクション
    public:
    CTalkButton();
    // アトリビュート
    public:
    // オペレーション
    public:
    // オーバーライド
    // ClassWizard は仮想関数のオーバーライドを生成します。
    //{{AFX_VIRTUAL(CTalkButton)
    public:
    virtual BOOL PreTranslateMessage(MSG* pMsg);
    //}}AFX_VIRTUAL
    // インプリメンテーション
    public:
    CToolTipCtrl ttc;
    virtual ~CTalkButton();
    // 生成されたメッセージ マップ関数
    protected:
    //{{AFX_MSG(CTalkButton)
    afx_msg void

    OnMouseMove(UINT nFlags, CPoint point);

    afx_msg int
    OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnTimer(UINT nIDEvent);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
    };
    //////////////////////////////////////////////////
    //{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ は前行の直前に追加の宣言
    //を挿入します。
    #endif // !defined(AFX_TALKBUTTON_H__
    1DC177F6_3055_45D1_9E99_0B07854838C4
    __INCLUDED_)

    使用法 CBitmapButton に対して追加したメンバ関数
    はハンドラだけなので使用方法は CBitmapButton と
    同じ。
    いつも View のメンバに入れて OnCreate() の
    中で Create する方法でしか使っていないので 違う使い
    方をしたときにどうなるかは分からない。
    CRect rect_hlp(0, 0, 32, 32);
    helpButton.Create("",

    BS_OWNERDRAW BS_PUSHBUTTON
    WS_CHILD WS_VISIBLE, rect_hlp, this,
    ID_ABOUT);
    helpButton.LoadBitmaps(IDB_HLP_FREE,

    IDB_HLP_PUSH, IDB_HLP_FREE, IDB_HLP_PUSH);
     ↑ な感じでいつも使っている。各パラメータの意味は

    CBitmapButton を参照。// TalkButton.cpp :
    インプリメンテーション ファイル
    //
    #include "stdafx.h"
    #include "TalkButton.h"
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
    ////////////////////////////////////////////////
    // CTalkButton
    CTalkButton::CTalkButton()
    {
    }
    CTalkButton::~CTalkButton()
    {
    }
    BEGIN_MESSAGE_MAP(CTalkButton,

    CButton)
    //{{AFX_MSG_MAP(CTalkButton)
    ON_WM_MOUSEMOVE()
    ON_WM_CREATE()
    ON_WM_TIMER()
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    ///////////////////////////////////////////////
    // CTalkButton メッセージ ハンドラ
    // ツールチップとステータスバーにツールヒントを出

    せるビットマップボタン
    int CTalkButton::OnCreate(LPCREATESTRUCT

    lpCreateStruct)
    {
    if (CButton::OnCreate(lpCreateStruct) == -1)
    return -1;
    ttc.Create(this); // 以下ツールチップの作成・登録
    ttc.Activate(TRUE);
    CRect rect;
    CWnd::GetClientRect(&rect);
    ttc.AddTool(this, (UINT)GetDlgCtrlID(), &rect,

    (UINT)GetDlgCtrlID());
    return 0;
    }
    BOOL CTalkButton::PreTranslateMessage(

    MSG* pMsg)
    {
    // ツールチップのおきまり
    if (m_hWnd)
    ttc.RelayEvent(pMsg);
    return CButton::PreTranslateMessage(pMsg);
    }
    void CTalkButton::OnMouseMove(UINT nFlags,

    CPoint point)
    {
    // 親ウインドウのステータスバーにツールヒントを表示する。
    if (GetCapture() == this) {
    CRect rect;
    CWnd::GetClientRect(&rect);
    if (!rect.PtInRect(point)) { // マウスがボタンの外

    に出たときに idle を出したいから
    KillTimer(999); // 自分の上にいるときだけキャ
    プチャしてるんだよ。
    ReleaseCapture(); // どんくさいだろ。
    GetParent()->GetParentFrame()->

    SetMessageText(AFX_IDS_IDLEMESSAGE);
    }
    } else {
    GetParent()->GetParentFrame()->

    SetMessageText((UINT)GetDlgCtrlID());
    SetCapture();
    SetTimer(999, 200, NULL); // 親ウインドウ監視タイマー
    }
    // 自作のツールバーにツールチップを出そうとしてえらい苦労した。
    // あんまりうまくいかないんで、あきらめてステータスバーの表示に
    // 変更したがそれでも大変だった。
    // この CTalkButton を CBitmapButton から派生させることで
    // 乗り切ったが、あとでツールチップも実装した。
    // 結局、派生クラスを作るんだったらツールチップのほうが楽だった。
    // 親ウインドウがバックグラウンドになったときにステータスバーを
    // idle に戻したかったのでまたタイマーに頼ってしまった。
    CButton::OnMouseMove(nFlags, point);
    }
    void CTalkButton::OnTimer(UINT nIDEvent)
    {
    // 親ウインドウがバックグラウンドになったときは

    キャプチャーをやめる
    if (GetForegroundWindow() != GetParent()->

    GetParentFrame()) {
    KillTimer(999); // またタイマー使っちゃったよ。
    ReleaseCapture(); // もっといいメッセージないの?
    GetParent()->GetParentFrame()->

    SetMessageText(AFX_IDS_IDLEMESSAGE);
    }
    CButton::OnTimer(nIDEvent);
    }




    星期二, 八月 29, 2006

    玩转keybd_event(转载)

    玩转keybd_event

    模拟键盘平时不是很常用, 但是当调用某些快捷键执行某项功能时, 它真的是那么的方便呀. 你不信? 看看下面的实现, 你就会大呼: 为什么不早点告诉我? 呵呵, 原来没有blog呀, 都靠这些挣分呢.

    1) 显示桌面:

    很多软件有显示桌面的功能, 并且大家的方法都是遍历窗口, 然后让它们最小化, 其实 win系统给咱们了一个非常方便的WIN键(就是键盘上在CTRL键和ALT键之间的那个带win标志的按键), 利用它, 可以轻松的完成显示桌面的功能.

    keybd_event(VK_LWIN, 0, 0 ,0);
    keybd_event('M', 0, 0 ,0);
    keybd_event('M', 0, KEYEVENTF_KEYUP ,0);
    keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP,0);

    其他的操作也类似, 比如直接显示开始的运行,就把上面的'M'换成'R'即可。

    直接 keybd_event(VK_LWIN, 0, 0 ,0);
    keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP,0);

    直接显示“开始”对话框了。

    2) 实现快速的全选

    很多的时候,比如listctrl实现全选,你可以用listctrl循环设置每一项的状态为选中,多罗索的事情呀。用快捷键试一试CTRL+A,其他的快捷键一样的用法,呵呵,你知道怎么办了吧?

    keybd_event(VK_CONTROL, (BYTE)0, 0 ,0);
    keybd_event('A',(BYTE)0, 0 ,0); //此处可以用 'A', (BYTE)65, 用'a'不起作用.
    keybd_event('A', (BYTE)0, KEYEVENTF_KEYUP,0);
    keybd_event(VK_CONTROL, (BYTE)0, KEYEVENTF_KEYUP,0);

    3) 执行某些特殊的键,比如数字键,大小写,下面是数字键的例子

    bool bState=true; //true为按下NumLock,false反之
    BYTE keyState[256];

    GetKeyboardState((LPBYTE)&keyState);
    if( (bState && !(keyState[VK_NUMLOCK] & 1))
    (!bState && (keyState[VK_NUMLOCK] & 1)) )
    {
    // Simulate a key press
    keybd_event( VK_NUMLOCK,
    0x45,
    KEYEVENTF_EXTENDEDKEY 0,
    0 );

    // Simulate a key release
    keybd_event( VK_NUMLOCK,
    0x45,
    KEYEVENTF_EXTENDEDKEY KEYEVENTF_KEYUP,
    0);
    }

    4) 你想CTRL+ALT+DELETE三键一起按下,

    keybd_event(VK_CONTROL, 0, 0 ,0);
    keybd_event(VK_MENU,0, 0 ,0);
    keybd_event(VK_DELETE,0, 0 ,0);

    keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP ,0);
    keybd_event(VK_MENU,0, KEYEVENTF_KEYUP ,0);
    keybd_event(VK_DELETE,0, KEYEVENTF_KEYUP ,0);
    呵呵,这样不会成功呀,因为这几个键直接是操作系统来截获执行的,而模拟键盘只能发向应用程序,所以这种方法不行的(想显示锁定对话框,用 LockWorkStation();)

    5) Window2000/NT/XP已经不提倡用这个函数了,上面的方法只是为了让大家开阔一下思路,怎么替代呢,呵呵,看下面,所以上面的所有代码都可以用这个来完成

    //2000下用这个代替 ,包含 "winable.h"
    INPUT input[4];
    memset(input, 0, sizeof(input));

    input[0].type = input[1].type = input[2].type = input[3].type = INPUT_KEYBOARD;

    input[0].ki.wVk = input[3].ki.wVk = VK_LWIN;
    input[1].ki.wVk = input[2].ki.wVk = 'R';


    //接下来释放它,这一点很重要。
    input[2].ki.dwFlags = input[3].ki.dwFlags = KEYEVENTF_KEYUP;
    input[0].ki.time = input[1].ki.time = input[2].ki.time = input[3].ki.time = GetTickCount();

    SendInput(4, input, sizeof(INPUT));

    感觉比那个有点罗索,呵呵。

    ====================

    附WIN键的部分快捷键:

    WIN键+D=快速的切到桌面,再次点击返回

    WIN键+E=快速打开资源管理器

    WIN键+R=“运行”。

    WIN键+M=全部视窗最小化。

    WIN键+Shift+M=取消全部视窗最小化。

    WIN键+F1=Help。

    WIN键+F=“寻找”。

    WIN键+Ctrl+F=显示“查找电脑”。

    WIN键+Tab=切换工作列的程式。

    WIN键+Break=显示系统内容。

    星期二, 八月 15, 2006

    获取当前路径

    GetCurrentDirectory只是获取当前的目录,当计算机重新启动后,当前的路径一般为系统目录,因此系统启动后,使用该函数只能获取当前的目录,而不是应用程序所在的目录。

    使用GetModuleFileName函数,GetModuleFileName()是一个得到路径的API函数。本函数将这个API函数封装在其中,为的是简化调用的目的。

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

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

    CString sPath;

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

    sPath.ReleaseBuffer ();

    int nPos;

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

    sPath=sPath.Left (nPos);



    星期三, 八月 02, 2006

    「Windows界面」ツールチップを表示

    用法
    ①、メンバー変数を追加(CToolTipCtrl m_tip)。
        private: CToolTipCtrl m_tip;

    ②、ダイアローグクラスのOnInitDialog()関数で以下の記述を追加
        EnableToolTips(TRUE); // ツールチップ利用可
        m_tip.Create(this); // コントロールを作成する
        m_tip.AddTool((CButton *)GetDlgItem(IDC_BUTTON1), "This is button"); // ツール ヒント コントロールにツールを登録します。

    ③、ダイアログクラスのPreTranslateMessage関数内でRelayEvent()を呼び出す
        m_tip.RelayEvent(pMsg)。

    星期二, 八月 01, 2006

    「Windows界面」解像度

    GetDeviceCaps() は、ディスプレイの DPI を取得するために使用できます

    星期五, 七月 21, 2006

    关于Jsp中的Application对象

    ServletContext
    ServletContext是定义在javax.servlet包中的对象。它定义了用于WEB应用中的服务器端组件关联servlet容器的方法集合。
    ServletContext经常被用于存储对象的区域,这些对象在WEB应用中的所有的服务器端组件中使用。你可以把ServletContext当作在WEB应用中共享的存储区域。把一个对象放置到ServletContext中时,它存在于WEB应用的整个生命周期中,除非它被明确的删除或替换。在ServletContext中定义了四个方法来实现存储区的共享功能。
    表2.1描述了四个方法:
    方法名
    描述
    setAttribute(String name,Object obj)
    通过名称绑定一个对象并存储对象到当前ServletContext。如果指定的名称已经被使用过,这个方法会删除旧对象绑定为新对象。
    getAttribute(String name)
    返回指定名称的对象,如果名称不存在返回null。
    removeAttribute(String name)
    从ServletContext中删除指定名称的对象

    getAttributeNames()

    返回在ServletContext中的指定名称的对象集合

    而ServletContext在jsp中所对应的就是application, application为ServletContext的一个引用

    示例:
    Servlet中
    // 获取ServletContext
    ServletContext context = getServletContext();
    // 获取
    String userName = (String)context.getAttribute("USERNAME");

    在对应的Jsp中
    String userName = (String)application.getAttribute("USERNAME");

    Windowsサービスの起動と停止

    net start "サービス名"
    net stop "サービス名"