先调用GetWindowRect后再调用ScreenToClient,这个时候得到的rect和直接使用GetClientRect得到的值是相等的。有时候需要获得窗口矩形的大小和客户区矩形的大小二者的值,故需要分别调用GetWindowRect和GetClientRect。如果只需要获得客户区矩形的大小,调用GetClientRect就行了。GetWindowRect和GetClientRect函数的说明如下:

CWnd::GetClientRect  
    void GetClientRect( LPRECT lpRect ) const;
Parameters:
lpRect
    Points to a RECT structure or a CRect object to receive the client coordinates. The left and top members will be 0. The right and bottom members will contain the width and height of the window.
Remarks:
    Copies the client coordinates of the CWnd client area into the structure pointed to by lpRect. The client coordinates specify the upper-left and lower-right corners of the client area. Since client coordinates are relative to the upper-left corners of the CWnd client area, the coordinates of the upper-left corner are (0,0).

CWnd::GetWindowRect
void GetWindowRect( LPRECT lpRect ) const;
Parameters:
lpRect
Points to a CRect object or a RECT structure that will receive the screen coordinates of the upper-left and lower-right corners.
Remarks:
Copies the dimensions of the bounding rectangle of the CWnd object to the structure pointed to by lpRect. The dimensions are given in screen coordinates relative to the upper-left corner of the display screen. The dimensions of the caption, border, and scroll bars, if present, are included.



GetWindowRect() 得到的是在屏幕坐标系下的RECT;(即以屏幕左上角为原点
GetClientRect() 得到的是在客户区坐标系下的RECT; (即以所在窗口左上角为原点

GetWindowRect()取的是整个窗口的矩形;
GetClientRect()取的仅是客户区的矩形,也就是说不包括标题栏,外框等;

第一个函数获得的是窗口在屏幕上的位置,得到的结果可能是这样CRect(10,10,240,240);
第二个函数和它不同,它只获得了客户区的大小,因此得到的结果总是这样CRect(0,0,width,height);

ScreenToClient() 就是把屏幕坐标系下的RECT坐标转换为客户区坐标系下的RECT坐标。

 

The GetClientRect function retrieves the coordinates of a window's client area. The client coordinates specify the upper-left and lower-right corners of the client area. Because client coordinates are relative to the upper-left corner of a window's client area, the coordinates of the upper-left corner are (0,0).

GetClientRect得到的是客户区的大小,也就是说这样得到的左上角永远是(0,0)

The GetWindowRect function retrieves the dimensions of the bounding rectangle of the specified window. The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen.

GetWindowRect 是窗口相对于整个屏幕的坐标,屏幕左上点为0,0

相互转化用ScreenToClient 或者 ClientToScreen

ClientToScreen
The ClientToScreen function converts the client coordinates of a specified point to screen coordinates.
BOOL ClientToScreen(
   HWND hWnd,        // window handle for source coordinates
   LPPOINT lpPoint   // pointer to structure containing screen coordinates
);

Parameters
hWnd
Handle to the window whose client area is used for the conversion.
lpPoint
Pointer to a POINT structure that contains the client coordinates to be converted. The new screen coordinates are copied into this structure if the function succeeds.
Return Values
If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero.


虽然存在调用GetWindowRect后再调用ScreenToClient==GetClientRect,但ScreenToClient()和ClientToScreen()两者都是属于WINDOWS API函数,可能是存在一定的冗余设计,但意义不同。
不过在.Net Framework下对WINDOWS API函数进行了重新整理和优化,在获取控件或窗口的屏幕坐标和客户区坐标时更方便的多,只需要得到与控件或窗口相对应屏幕坐标和客户区坐标属性值就可以了

ScreenToClient
The ScreenToClient function converts the screen coordinates of a specified point on the screen to client coordinates.
BOOL ScreenToClient(
   HWND hWnd,         // window handle for source coordinates
   LPPOINT lpPoint    // address of structure containing coordinates
);
Parameters:
hWnd
Handle to the window whose client area will be used for the conversion.
lpPoint
Pointer to a POINT structure that contains the screen coordinates to be converted.
Return Values:
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.
posted @ 2011-04-01 11:30 wrh 阅读(908) | 评论 (1)编辑 收藏
Creating   a   Template   in   Memory
Applications   sometimes   adapt   or   modify   the   content   of   dialog   boxes   depending   on   the   current   state   of   the   data   being   processed.   In   such   cases,   it   is   not   practical   to   provide   all   possible   dialog   box   templates   as   resources   in   the   application 's   executable   file.   But   creating   templates   in   memory   gives   the   application   more   flexibility   to   adapt   to   any   circumstances.  

In   the   following   example,   the   application   creates   a   template   in   memory   for   a   modal   dialog   box   that   contains   a   message   and   OK   and   Help   buttons.  

In   a   dialog   template,   all   character   strings,   such   as   the   dialog   box   and   button   titles,   must   be   Unicode   strings.   This   example   uses   the   MultiByteToWideChar   function   to   generate   these   Unicode   strings,   because   Windows   95/98   and   Windows   NT/Windows   2000   support   MultiByteToWideChar  

The   DLGITEMTEMPLATE   structures   in   a   dialog   template   must   be   aligned   on   DWORD   boundaries.   To   align   these   structures,   this   example   uses   a   helper   routine   that   takes   an   input   pointer   and   returns   the   closest   pointer   that   is   aligned   on   a   DWORD   boundary.

#define   ID_HELP       150
#define   ID_TEXT       200

LPWORD   lpwAlign   (   LPWORD   lpIn)
{
        ULONG   ul;

        ul   =   (ULONG)   lpIn;
        ul   +=3;
        ul   > > =2;
        ul   < <=2;
        return   (LPWORD)   ul;
}

LRESULT   DisplayMyMessage(HINSTANCE   hinst,   HWND   hwndOwner,  
        LPSTR   lpszMessage)
{
        HGLOBAL   hgbl;
        LPDLGTEMPLATE   lpdt;
        LPDLGITEMTEMPLATE   lpdit;
        LPWORD   lpw;
        LPWSTR   lpwsz;
        LRESULT   ret;
        int   nchar;

        hgbl   =   GlobalAlloc(GMEM_ZEROINIT,   1024);
        if   (!hgbl)
                return   -1;
 
        lpdt   =   (LPDLGTEMPLATE)GlobalLock(hgbl);
 
        //   Define   a   dialog   box.
 
        lpdt-> style   =   WS_POPUP   |   WS_BORDER   |   WS_SYSMENU
                                      |   DS_MODALFRAME   |   WS_CAPTION;
        lpdt-> cdit   =   3;     //   number   of   controls
        lpdt-> x     =   10;     lpdt-> y     =   10;
        lpdt-> cx   =   100;   lpdt-> cy   =   100;

        lpw   =   (LPWORD)   (lpdt   +   1);
        *lpw++   =   0;       //   no   menu
        *lpw++   =   0;       //   predefined   dialog   box   class   (by   default)

        lpwsz   =   (LPWSTR)   lpw;
        nchar   =   1+   MultiByteToWideChar   (CP_ACP,   0,   "My   Dialog ",  
                                                                        -1,   lpwsz,   50);
        lpw       +=   nchar;

        //-----------------------
        //   Define   an   OK   button.
        //-----------------------
        lpw   =   lpwAlign   (lpw);   //   align   DLGITEMTEMPLATE   on   DWORD   boundary
        lpdit   =   (LPDLGITEMTEMPLATE)   lpw;
        lpdit-> x     =   10;   lpdit-> y     =   70;
        lpdit-> cx   =   80;   lpdit-> cy   =   20;
        lpdit-> id   =   IDOK;     //   OK   button   identifier
        lpdit-> style   =   WS_CHILD   |   WS_VISIBLE   |   BS_DEFPUSHBUTTON;

        lpw   =   (LPWORD)   (lpdit   +   1);
        *lpw++   =   0xFFFF;
        *lpw++   =   0x0080;         //   button   class

        lpwsz   =   (LPWSTR)   lpw;
        nchar   =   1+MultiByteToWideChar   (CP_ACP,   0,   "OK ",   -1,   lpwsz,   50);
        lpw       +=   nchar;
        lpw   =   lpwAlign   (lpw);   //   align   creation   data   on   DWORD   boundary
        *lpw++   =   0;                       //   no   creation   data

        //-----------------------
        //   Define   a   Help   button.
        //-----------------------
        lpw   =   lpwAlign   (lpw);   //   align   DLGITEMTEMPLATE   on   DWORD   boundary
        lpdit   =   (LPDLGITEMTEMPLATE)   lpw;
        lpdit-> x     =   55;   lpdit-> y     =   10;
        lpdit-> cx   =   40;   lpdit-> cy   =   20;
        lpdit-> id   =   ID_HELP;         //   Help   button   identifier
        lpdit-> style   =   WS_CHILD   |   WS_VISIBLE   |   BS_PUSHBUTTON;

        lpw   =   (LPWORD)   (lpdit   +   1);
        *lpw++   =   0xFFFF;
        *lpw++   =   0x0080;                                   //   button   class   atom

        lpwsz   =   (LPWSTR)   lpw;
        nchar   =   1+MultiByteToWideChar   (CP_ACP,   0,   "Help ",   -1,   lpwsz,   50);
        lpw       +=   nchar;
        lpw   =   lpwAlign   (lpw);   //   align   creation   data   on   DWORD   boundary
        *lpw++   =   0;                       //   no   creation   data

        //-----------------------
        //   Define   a   static   text   control.
        //-----------------------
        lpw   =   lpwAlign   (lpw);   //   align   DLGITEMTEMPLATE   on   DWORD   boundary
        lpdit   =   (LPDLGITEMTEMPLATE)   lpw;
        lpdit-> x     =   10;   lpdit-> y     =   10;
        lpdit-> cx   =   40;   lpdit-> cy   =   20;
        lpdit-> id   =   ID_TEXT;     //   text   identifier
        lpdit-> style   =   WS_CHILD   |   WS_VISIBLE   |   SS_LEFT;

        lpw   =   (LPWORD)   (lpdit   +   1);
        *lpw++   =   0xFFFF;
        *lpw++   =   0x0082;                                                   //   static   class

        for   (lpwsz   =   (LPWSTR)lpw;        
                *lpwsz++   =   (WCHAR)   *lpszMessage++;
        );
        lpw   =   (LPWORD)lpwsz;
        lpw   =   lpwAlign   (lpw);   //   align   creation   data   on   DWORD   boundary
        *lpw++   =   0;                       //   no   creation   data

        GlobalUnlock(hgbl);  
        ret   =   DialogBoxIndirect(hinst,   (LPDLGTEMPLATE)   hgbl,  
                hwndOwner,   (DLGPROC)   DialogProc);  
        GlobalFree(hgbl);  
        return   ret;  
}
posted @ 2011-03-24 10:53 wrh 阅读(598) | 评论 (0)编辑 收藏

各位经验丰富的学者专家老师们:
    你们好!
    大家都知道大字体gbcbig.shp是AutoCAD本地化时需要的一个字体定义形文件,其转化为gbcbig.shx后可被AutoCAD调用。现在我正在做一项工作,想将这种.SHX文件转换成.aa文件,以扩展.shx格式文件的使用范围,说明一下:.aa文件是ThinkDesign中的字体文件定义格式。我想实现他们三者(.shp,.shx,.aa)之间的相互转化.目前已经基本实现了这个目标,具体包括:shx-->shp  shp-->aa
shx-->aa 。在实现将shp-->aa/shx-->aa 中,我需要知道.shp/.shx文件中字高的定义。
在AutoCAD帮助文件中有如下解释:
*0,4,font-name
above,below,modes,0
其中,above 值指出大写字母从基线向上延伸的矢量长度,below 指出小写字母从基线向下延伸的距离。基线的概念与纸面书写时的基准线相似。这些值定义了基本字符尺寸,用作 TEXT 命令中指定高度的缩放比例。
举个例子:
*BIGFONT 7019,1,161,254
*0,4,工程汉字字符集 1998.1. FAW/Autodesk CTC CBX(0293)
0,64,2,0
在实现将shp转化aa和将shx转化aa 中,我将字高规定为max(above,below),在gbcbig中具体表现为max(0,64),除了gbcbig这个字体定义外,其他都还算合适,问题就出在gbcbig上,因为gbcbig中有一个子形*142,具体是这样定义的:
*142,14,起始描述(比例系数加入和起点调整)
4,9,3,102,2,14,8,(-34,-80),2,8,(0,-5),0
这就有一个问题,如果我把max(0,64)当成是字高的话,对于一个具体的字的定义来说,比如*48112,57,火
7,142,5,2,8,(29,51),1,8,(-8,-30),8,(-15,-18),2,8,(49,5),1,8,(7,0),2,8,(-29,81),
1,8,(-4,-40),2,8,(-17,19),1,8,(9,-21),2,8,(10,7),1,8,(24,-47),2,8,(-7,61),1,8,(-13,-19),7,143,0
这个字在CAD中显示出来就是一个正常显示的9/102大小的字,非常的小,难以辨识。
我现在需要解决这个字体缩放问题。为什么这个gbcbig字体显示结果会是这样小呢,我怀疑是我把字高的定义搞错了,查了很多资料,也没有答案,特请大家帮忙。
                    
问题1:字体的形定义中字高是如何定义的
问题2:为什么一般的普通大字体(非扩展大字体)文件,比如
*BIGFONT 5887,1,128,255
*0,4,FS791127 Copyright (c) 1991 by Top One Technology Inc., Taipei., Taiwan.
48,0,2,0
*BIGFONT 8009,3,161,163,166,166,176,247
*0,4,HZ 1/6/92
127,0,2,0
中的编码0的定义部分前两位是:第一位是非0,第二位是0;而gbcbig中编码0的定义部分前两位却是反过来的:0,64?是偶然还是另有其他解释呢?







一问:为什么不能显示汉字?或输入的汉字变成了问号
答:原因可能是:
1. 对应的字型没有使用汉字字体,如HZTXT.SHX等;
2. 当前系统中没有汉字字体形文件;应将所用到的形文件复制AutoCAD的字体目录中(一般为...\FONTS\);
3. 对于某些符号,如希腊字母等,同样必须使用对应的字体形文件,否则会显示成?号。

二问:为什么输入的文字高度无法改变
答:使用的字型的高度值不为0时,
DTEXT命令书写文本时都不提示输入高度
这样写出来的文本高度是不变的
包括使用该字型进行的尺寸标注。

三问:如何改变已经存在的字体格式?
答:如果想改变已有文字的大小、字体、高宽比例、间距、倾斜角度、插入点等,
最好利用“特性DDMODIFY)”命令(前提是你已经定义好了许多文字格式)。
点击“特性”命令,点击要修改的文字,回车,
出现“修改文字”窗口,
选择要修改的项目进行修改即可。

四问:可以使用系统字体吗,如宋体、楷体等?
答:虽然ACAD R14能够使用*.TTF汉字字体文件,
可是这种字体太花费CPU时间,
对于档次稍低的电脑,
显示速度(如使用实时缩放命令时)实在太慢。
建议档次低的电脑还是使用*.shx文件好。
在汉字*.shx文件当中,
推荐使用大恒或浩辰公司为ACADR12配套的HZ*.shx字体文件,
但在不同机器上没有相应字体会引起汉字显示问题。
当然为美观起见少量使用系统字库也未尝不可。

五问 何替换找不到的原文字体?
复制要替换的字库为将被替换的字库名,如:打开一幅图,提示未找到字体jd,你想用hztxt.shx替换它,那么你可以去找AutoCAD字体文件夹(font)把里面的hztxt.shx 复制一份,重新命名为jd.shx,然后在把XX.shx放到font里面,在重新打开此图就可以了。以后如果你打开的图包含jd这样你机子里没有的字体,就再也不会不停的要你找字体替换了。

posted @ 2011-03-23 09:42 wrh 阅读(2978) | 评论 (0)编辑 收藏

使用字体的基本术语,会让设计过程中的交流变得非常容易。这里是一些基本的术语,让你的交流能够更深入,而不是“嗯,那里,那个,那个黑色的小玩意……”小写e的"字怀"(“字谷”)有时候也被称为是字“眼”,如果你还想更全面的了解其他术语,可以去图书馆,或者是参考下面的网络资源。(译注:很遗憾,原文中链接的一些资源已经无法访问了。但我们可以用google找到其他的资源。这些术语很重要,如果你希望阅读西方的第一手的字体设计研究资料,这是必须跨越的一关。但目前在国内的设计界似乎还没有一个统一的翻译,不少书籍的翻译都是各行其是,让读者也无所适从。有时间我会找一篇比较完整的来翻译。)

http://www.adobe.com/type/topics/glossary.html
http://gmunch.home.pipeline.com/typo-L/faq/anat.htm
http://www.google.cn/search?client=aff-cs-maxthon&ie=UTF-8&oe=UTF-8&hl=zh-CN&q=typography%20anatomy&um=1&sa=N&tab=iw

posted @ 2011-03-18 15:15 wrh 阅读(251) | 评论 (0)编辑 收藏

所有的汉字或者英文都是下面的原理,
由左至右,每8个点占用一个字节,最后不足8个字节的占用一个字节,而且从最高位向最低位排列。
生成的字库说明:(以12×12例子)


一个汉字占用字节数:12÷8=1····4也就是占用了2×12=24个字节。
编码排序A0A0→A0FE A1A0→A2FE依次排列。
以12×12字库的“我”为例:“我”的编码为CED2,所以在汉字排在CEH-AOH=2EH区的D2H-A0H=32H个。所以在12×12字库的起始位置就是[{FE-A0}*2EH+32H]*24=104976开始的24个字节就是我的点阵模。
其他的类推即可。
英文点阵也是如此推理。

在DOS程序中使用点阵字库的方法

回到顶部

    首先需要理解的是点阵字库是一个数据文件,在这个数据文件里面保存了所有文字的点阵数据.至于什么是点阵,我想我不讲大家都知道 的,使用过"文曲星"之类的电子辞典吧,那个的液晶显示器上面显示的汉子就能够明显的看出"点阵"的痕迹.在 PC 机上也是如此,文字也是由点阵来组成了,不同的是,PC机显示器的显示分辨率更高,高到了我们肉眼无法区分的地步,因此"点阵"的痕迹也就不那么明显了.

    点阵、矩阵、位图这三个概念在本质上是有联系的,从某种程度上来讲,这三个就是同义词.点阵从本质上讲就是单色位图,他使用一个比特来表示一个点,如果这 个比特为0,表示某个位置没有点,如果为1表示某个位置有点.矩阵和位图有着密不可分的联系,矩阵其实是位图的数学抽象,是一个二维的阵列.位图就是这种 二维的阵列,这个阵列中的 (x,y) 位置上的数据代表的就是对原始图形进行采样量化后的颜色值.但是,另一方面,我们要面对的问题是,计算机中数据的存放都是一维的,线性的.因此,我们需要 将二维的数据线性化到一维里面去.通常的做法就是将二维数据按行顺序的存放,这样就线性化到了一维.

    那么点阵字的数据存放细节到底是怎么样的呢.其实也十分的简单,举个例子最能说明问题.比如说 16*16 的点阵,也就是说每一行有16个点,由于一个点使用一个比特来表示,如果这个比特的值为1,则表示这个位置有点,如果这个比特的值为0,则表示这个位置没 有点,那么一行也就需要16个比特,而8个比特就是一个字节,也就是说,这个点阵中,一行的数据需要两个字节来存放.第一行的前八个点的数据存放在点阵数 据的第一个字节里面,第一行的后面八个点的数据存放在点阵数据的第二个字节里面,第二行的前八个点的数据存放在点阵数据的第三个字节里面,…,然后后 面的就以此类推了.这样我们可以计算出存放一个点阵总共需要32个字节.看看下面这个图形化的例子:

    | |1| | | | | | | | | | |1| | | |

    | | |1|1| |1|1|1|1|1|1|1|1|1| | |

    | | | |1| | | | | | | | |1| | | |

    |1| | | | | |1| | | | | |1| | | |

    | |1|1| | | |1| | | | | |1| | | |

    | | |1| | | |1| | | | |1| | | | |

    | | | | |1| | |1| | | |1| | | | |

    | | | |1| | | |1| | |1| | | | | |

    | | |1| | | | | |1| |1| | | | | |

    |1|1|1| | | | | | |1| | | | | | |

    | | |1| | | | | |1| |1| | | | | |

    | | |1| | | | |1| | | |1| | | | |

    | | |1| | | |1| | | | | |1| | | |

    | | |1| | |1| | | | | | |1|1|1| |

    | | | | |1| | | | | | | | |1| | |

    | | | | | | | | | | | | | | | | |

    可以看出这是一个"汉"字的点阵,当然文本的方式效果不是很好.根据上面的原则,我们可以写出这个点阵的点阵数 据:0x40,0x08,0x37,0xfc,0x10,0x08,…, 当然写这个确实很麻烦所以我不再继续下去.我这样做,也只是为了向你说明,在点阵字库中,每一个点阵的数据就是按照这种方式存放的.

    当然也存在着不规则的点阵,这里说的不规则,指的是点阵的宽度不是8的倍数,比如 12*12 的点阵,那么这样的点阵数据又是如何存放的呢?其实也很简单,每一行的前面8个点存放在一个字节里面,每一行的剩下的4点就使用一个字节来存放,也就是说 剩下的4个点将占用一个字节的高4位,而这个字节的低4位没有使用,全部都默认的为零.这样做当然显得有点浪费,不过却能够便于我们进行存放和寻址.对于 其他不规则的点阵,也是按照这个原则进行处理的.这样我们可以得出一个 m*n 的点阵所占用的字节数为 (m+7)/8*n.

    在明白了以上所讲的以后,我们可以写出一个显示一个任意大小的点阵字模的函数,这个函数的功能是输出一个宽度为w,高度为h的字模到屏幕的 (x,y) 坐标出,文字的颜色为 color,文字的点阵数据为 pdata 所指:

    /*输出字模的函数*/

    void _draw_model(char *pdata, int w, int h, int x, int y, int color)

    {

    int     i;    /* 控制行 */

    int     j;    /* 控制一行中的8个点 */

    int     k;    /* 一行中的第几个"8个点"了 */

    int     nc;   /* 到点阵数据的第几个字节了 */

    int     cols; /* 控制列 */

    BYTE    static mask[8]={128, 64, 32, 16, 8, 4, 2, 1}; /* 位屏蔽字 */

    w = (w + 7) / 8 * 8; /* 重新计算w */

    nc = 0;

    for (i=0; i<h; i++)

    {

    cols = 0;

    for (k=0; k<w/8; k++)

    {

    for (j=0; j<8; j++)

    {

    if (pdata[nc]&mask[j])

    putpixel(x+cols, y+i, color);

    cols++;

    }

    nc++;

    }

    }

    }

    代码很简单,不用怎么讲解就能看懂,代码可能不是最优化的,但是应该是最易读懂的.其中的 putpixel 函数,使用的是TC提供的 Graphics 中的画点函数.使用这个函数就可以完成点阵任意大小的点阵字模的输出.

    接下来的问题就是如何在汉子库中寻址某个汉子的点阵数据了.要解决这个问题,首先需要了解汉字在计算机中是如何表示的.在计算机中英文可以使用 ASCII 码来表示,而汉字使用的是扩展 ASCII 码,并且使用两个扩展 ASCII 码来表示一个汉字.一个 ASCII 码使用一个字节表示,所谓扩展 ASCII 码,也就是 ASCII 码的最高位是1的 ASCII 码,简单的说就是码值大于等于 128 的 ASCII 码.一个汉字由两个扩展 ASCII 码组成,第一个扩展 ASCII 码用来存放区码,第二个扩展 ASCII 码用来存放位码.在 GB2312-80 标准中,将所有的汉字分为94个区,每个区有94个位可以存放94个汉字,形成了人们常说的区位码,这样总共就有 94*94=8836 个汉字.在点阵字库中,汉字点阵数据就是按照这个区位的顺序来存放的,也就是最先存放的是第一个区的汉字点阵数据,在每一个区中有是按照位的顺序来存放 的.在汉字的内码中,汉字区位码的存放实在扩展 ASCII 基础上存放的,并且将区码和位码都加上了32,然后存放在两个扩展 ASCII 码中.具体的说就是:

    第一个扩展ASCII码 = 128+32 + 汉字区码

    第二个扩展ASCII吗 = 128+32 + 汉字位码

    如果用char hz[2]来表示一个汉字,那么我可以计算出这个汉字的区位码为:

    区码 = hz[0] - 128 - 32 = hz[0] - 160

    位码 = hz[1] - 128 - 32 = hz[1] - 160.

    这样,我们可以根据区位码在文件中进行殉职了,寻址公式如下:

    汉字点阵数据在字库文件中的偏移 = ((区码-1) * 94 + 位码) * 一个点阵字模占用的字节数

    在寻址以后,即可读取汉字的点阵数据到缓冲区进行显示了.以下是实现代码:

    /* 输出一个汉字的函数 */

    void _draw_hz(char hz[2], FILE *fp, int x, int y, int w, int h, int color)

    {

    char fONtbuf[128];   /* 足够大的缓冲区,也可以动态分配 */

    int ch0 = (BYTE)hz[0]-0xA0; /* 区码 */

    int ch1 = (BYTE)hz[1]-0xA0; /* 位码 */

    /* 计算偏移 */

    long offset = (long)pf->_hz_buf_size * ((ch0 - 1) * 94 + ch1 - 1);

    fseek(fp, offset, SEEK_SET);              /* 进行寻址 */

    fread(fontbuf, 1, (w + 7) / 8 * h, fp);   /* 读入点阵数据 */

    _draw_model(fontbuf, w, h, x, y, color); /* 绘制字模 */

    }

    以上介绍完了中文点阵字库的原理,当然还有英文点阵字库了.英文点阵字库中单个点阵字模数据的存放方式与中文是一模一样的,也就是对我们所写的 _draw_model 函数同样可以使用到英文字库中.唯一不同的是对点阵字库的寻址上.英文使用的就是 ASCII 码,其码值是0到127,寻址公式为:

    英文点阵数据在英文点阵字库中的偏移 = 英文的ASCII码 * 一个英文字模占用的字节数

    可以看到,区分中英文的关键就是,一个字符是 ASCII 码还是扩展 ASCII 码,如果是 ASCII 码,其范围是0到127,这样是使用的英文字库,如果是扩展 ASCII 码,则与其后的另一个扩展 ASCII 码组成汉字内码,使用中文字库进行显示.只要正确区分 ASCII 码的类型并进行分别的处理,也就能实现中英文字符串的混合输出了.

点阵字库和矢量字库的差别

回到顶部

    我们都只知道,各种字符在电脑屏幕上都是以一些点来表示的,因此也叫点阵.最早的字库就是直接把这些点存储起来,就是点阵字库.常见的汉字点阵字库有 16x16, 24x24 等.点阵字库也有很多种,主要区别在于其中存储编码的方式不同.点阵字库的最大缺点就是它是固定分辨率的,也就是每种字库都有固定的大小尺寸,在原始尺寸下使用,效果很好,但如果将其放大或缩小使用,效果就很糟糕了,就会出现我们通常说的锯齿现象.因为需要的字体大小组合有无数种,我们也不可能为每种大小都定义一个点阵字库.于是就出现了矢量字库.

    矢量字库

    矢量字库是把每个字符的笔划分解成各种直线和曲线,然后记下这些直线和曲线的参数,在显示的时候,再根据具体的尺寸大小,画出这些线条,就还原了原来的字符.它的好处就是可以随意放大缩小而不失真.而且所需存储量和字符大小无关.矢量字库有很多种,区别在于他们采用的不同数学模型来描述组成字符的线条.常见的矢量字库有 Type1字库和Truetype字库.

    在点阵字库中,每个字符由一个位图表示(如图2.5所示),并把它用一个称为字符掩膜的矩阵来表示,其中的每个元素都是一位二进制数,如果该位为1表示字符的笔画经过此位,该像素置为字符颜色;如果该位为0,表示字符的笔画不经过此位,该像素置为背景颜色.点阵字符的显示分为两步:首先从字库中将它的位图检索出来,然后将检索到的位图写到帧缓冲器中.

    在实际应用中,同一个字符有多种字体(如宋体、楷体等),每种字体又有多种大小型号,因此字库的存储空间十分庞大.为了减少存储空间,一般采用压缩技术.

    矢量字符记录字符的笔画信息而不是整个位图,具有存储空间小,美观、变换方便等优点.例如:在AutoCAD中使用图形实体-形(Shape)-来定义矢量字符,其中,采用了直线和圆弧作为基本的笔画来对矢量字符进行描述. 对于字符的旋转、放大、缩小等几何变换,点阵字符需要对其位图中的每个象素进行变换,而矢量字符则只需要对其几何图素进行变换就可以了,例如:对直线笔画的两个端点进行变换,对圆弧的起点、终点、半径和圆心进行变换等等.

    矢量字符的显示也分为两步.首先从字库中将它的字符信息.然后取出端点坐标,对其进行适当的几何变换,再根据各端点的标志显示出字符.

    轮廓字形法是当今国际上最流行的一种字符表示方法,其压缩比大,且能保证字符质量.轮廓字形法采用直线、B样条/Bezier曲线的集合来描述一个字符的轮廓线.轮廓线构成一个或若干个封闭的平面区域.轮廓线定义加上一些指示横宽、竖宽、基点、基线等等控制信息就构成了字符的压缩数据.

如何使用Windows的系统字库生成点阵字库?

回到顶部

    我的程序现在只能预览一个汉字的不同字体的点阵表达.

    界面很简单:   一个输出点阵大小的选择列表(8x8,16x16,24x24等),一个系统中已有的字体名称列表,一个预览按钮,一块画图显示区域.

    得到字体列表的方法:(作者称这一段是用来取回系统的字体,然后添加到下拉框中)

    //取字体名称列表的回调函数,使用前要声明一下该方法

    int   CALLBACK   MyEnumFONtProc(ENUMLOGFONTEX*   lpelf,NEWTEXTMETRICEX*   lpntm,DWORD   nFontType,long   lParam)

    {

    CFontPeekerDlg*   pWnd=(CFontPeekerDlg*)   lParam;

    if(pWnd)

    {

    if(   pWnd->m_combo_sfont.FindSTring(0,   lpelf->elfLogFont.lfFaceName)   <0   )

    pWnd->m_combo_sfont.AddString(lpelf->elfLogFont.lfFaceName);

    return   1;

    }

    return   0;

    }

    //说明:CFontPeekerDlg   是我的dialog的类名,   m_combo_sfont是列表名称下拉combobox关联的control变量

    //调用的地方     (******问题1:下面那个&lf怎么得到呢……)

    {

    ::EnumFontFamiliesEx((HDC)   dc,&lf,   (FONTENUMPROC)MyEnumFontProc,(LPARAM)   this,0);

    m_combo_sfont.SetCurSel(0);

    }

    字体预览:

    如果点阵大小选择16,显示的时候就画出16x16个方格.自定义一个类CMyStatic继承自CStatic,用来画图.在CMyStatic的OnPaint()函数中计算并显示.

    取得字体:

    常用的方法:用CreateFont创建字体,把字TextOut再用GetPixel()取点存入数组.   缺点:必须把字TextOut出来,能在屏幕上看见,不爽.

    我的方法,用这个函数:GetGlyphOutline(),可以得到一个字的轮廓矢量或者位图.可以不用textout到屏幕,直接取得字模信息

    函数原型如下:

    DWORD   GetGlyphOutline(

    HDC   hdc,                     //画图设备句柄

    UINT   uChar,                 //将要读取的字符/汉字

    UINT   uFormat,             //返回数据的格式(字的外形轮廓还是字的位图)

    LPGLYPHMETRICS   lpgm,     //   GLYPHMETRICS结构地址,输出参数

    DWORD   cbBuffer,       //输出数据缓冲区的大小

    LPVOID   lpvBuffer,     //输出数据缓冲区的地址

    CONST   MAT2   *lpmat2   //转置矩阵的地址

    );

    说明:

    uChar字符需要判断是否是汉字还是英文字符.中文占2个字节长度.

    lpgm是输出函数,调用GetGlyphOutline()是无须给lpgm   赋值.

    lpmat2如果不需要转置,将   eM11.value=1;   eM22.value=1;   即可.

    cbBuffer缓冲区的大小,可以先通过调用GetGlyphOutline(……lpgm,   0,   NULL,   mat);   来取得,然后动态分配lpvBuffer,再一次调用GetGlyphOutline,将信息存到lpvBuffer.   使用完毕后再释放lpvBuffer.

    程序示例:(***问题2:用这段程序,我获取的字符点阵总都是一样的,不管什么字……)

    ……前面部分省略……

    GLYPHMETRICS   glyph;

    MAT2   m2;

    memset(&m2,   0,   sizeof(MAT2));

    m2.eM11.value   =   1;

    m2.eM22.value   =   1;

    //取得buffer的大小

    DWORD   cbBuf   =   dc.GetGlyphOutline(   nChar,   GGO_BITMAP,   &glyph,   0L,   NULL,   &m2);

    BYTE*   pBuf=NULL;

    //返回GDI_ERROR表示失败.

    if(   cbBuf   !=   GDI_ERROR   )

    {

    pBuf   =   new   BYTE[cbBuf];

    //输出位图GGO_BITMAP   的信息.输出信息4字节(DWORD)对齐

    dc.GetGlyphOutline(   nChar,   GGO_BITMAP,   &glyph,   cbBuf,   pBuf,   &m2);

    }

    else

    {

    if(m_pFont!=NULL)

    delete   m_pFont;

    return;

    }

    编程中遇到问题:

    一开始,GetGlyphOutline总是返回-1,getLastError显示是"无法完成的功能",后来发现是因为调用之前没有给hdc设置Font.

    后来能取得pBuf信息后,又开始郁闷,因为不太明白bitmap的结果是按什么排列的.后来跟踪汉字"一"来调试(这个字简单),注意到了glyph.gmBlackBoxX   其实就是输出位图的宽度,glyph.gmBlackBoxY就是高度.如果gmBlackBoxX=15,glyph.gmBlackBoxY=2,表示输出的pBuf中有这些信息:位图有2行信息,每一行使用15   bit来存储信息.

    例如:我读取"一":glyph.gmBlackBoxX   =   0x0e,glyph.gmBlackBoxY=0x2;     pBuf长度cbBuf=8   字节

    pBuf信息:       00   08   00   00   ff   fc   00   00

    字符宽度   0x0e=14     则   第一行信息为:           0000   0000   0000   100       (只取到前14位)

    第二行根据4字节对齐的规则,从0xff开始         1111   1111   1111   110

    看出"一"字了吗?呵呵

    直到他的存储之后就可以动手解析输出的信息了.

    我定义了一个宏#define   BIT(n)     (1<<(n))     用来比较每一个位信息时使用

    后来又遇到了一个问题,就是小头和大头的问题了.在我的机器上是little   endian的形式,如果我用

    unsigned   long   *lptr   =   (unsigned   long*)pBuf;

    //j   from   0   to   15

    if(   *lptr   &   BIT(j)   )

    {

    //这时候如果想用j来表示写1的位数,就错了

    }

    因为从字节数组中转化成unsigned   long型的时候,数值已经经过转化了,像上例中,实际上是0x0800   在同BIT(j)比较.

    不多说了,比较之前转化一下就可以了if(   htonl(*lptr)   &   BIT(j)   )

Unicode中文点阵字库的生成与使用

回到顶部

    点阵字库包含两部分信息.首先是点阵字库文件头信息,它包含点阵字库文字的字号、多少位表示一个像素,英文字母与符号的size、起始和结束unicode编码、在文件中的起始偏移,汉字的size、起始和结束unicode编码、在文件中的起始偏移.然后是真实的点阵数据,即一段段二进制串,每一串表示一个字母、符号或汉字的点阵信息.

    要生成点阵字库必须有文字图形的来源,我的方法是使用ttf字体.ttf字体的显示采用的是SDL_ttf库,这是开源图形库SDL的一个扩展库,它使用的是libfreetype以读取和绘制ttf字体.

    它提供了一个函数,通过传入一个Unicode编码便能输出相应的文字的带有alpha通道的位图.那么我们可以扫描这个位图以得到相应文字的点阵信息.由于带有alpha通道,我们可以在点阵信息中也加入权值,使得点阵字库也有反走样效果.我采用两位来表示一个点,这样会有三级灰度(还有一个表示透明).

    点阵字库的显示首先需要将文件头信息读取出来,然后根据unicode编码判断在哪个区间内,然后用unicode编码减去此区间的起始unicode编码,算出相对偏移,并加上此区间的文件起始偏移得到文件的绝对偏移,然后读出相应位数的数据,最后通过扫描这段二进制串,在屏幕的相应位置输出点阵字型.

    显示点阵字体需要频繁读取文件,因此最好做一个固定大小的缓存,采用LRU置换算法维护此缓存,以减少磁盘读取.

标准点阵字库芯片

回到顶部

标准点阵字库芯片的特点:

    1.内涵全国信标委授权的标准点阵字型数据、

    2.支持国标字符集GB2312(6,763汉字),GB18030(27,484汉字).

    3.支持多种点阵字型,包括11×12点,15×16点,24×24点,32×32点.

    4.免除了字库烧录和测试工序,并节省了2%以上的烧录损耗.

    5.价格相当于空白FLASH价格

标准点阵字库芯片的种类和应用

回到顶部

51单片机的13×14点阵缩码汉卡

回到顶部

    我们历时数载,开发成"51单片机13×14点阵缩码汉卡",适用于目前国内外应用最为广泛的MCSX-51及其兼容系列单片机.

    与此同时,还开发了13×14点阵汉字字模.13×14点阵字模,可完全与目前通用的16×16点阵汉字字模媲美,其在单片机和嵌入式系统的汉字显示应用中也具有明显的经济价值和实用意义.

    1.单片机目前的汉字显示

    信息交流的最主要方式之一即文字交流,但由于我国方块汉字数量繁多,构形迥异,使汉字显示一直是我国计算机普及的障碍.随着计算机技术的迅速发展,PC机的汉字显示已不成问题.但对于成本低、体积小、应用灵活且用量极为巨大的单片机而言,因其结构简单,硬件资源十分有限,其汉字显示仍面对着捉襟见肘,力不从心的窘境.

    目前单片机的汉字显示有三种基本方法.

    ①采用标准字库法.即将国标汉字库固人ROM中,将单片机的硬件和软件进行特别扩展后以显示汉字.众所周知,即使是16×16点阵标准字库,也须占用200KB以上的单元内存,而就目前主流5l系列单片机而言,最大寻址范围仅64KB,即使程序区与数据区合起来也仅128KB内存.因此,若不加特别的扩展设计,不要说检字程序和用户空间,仅字库都装不下.这种方法虽然可以方便地使用现成标准字库,但却需占用大量的硬件和软件资源,增加很大一部分成本和设计难度,所以不经常使用.

    ②字模直接固化法.即将所显示的汉字,依先后顺序将其字模一一从标准字库中提取后,重新固化,予以显示.此法虽为简捷,但只适于显示少量汉字,且字模的制取繁琐,软件的修改维护都很困难.

    ③带索引小字库法.即将欲显示文件中的汉字字模,从标准字库中逐一提取固化,制成小型字库,并按其在小字库中的位置制成索引表,显示时从索引表查出其新的字模取码地址,取码显示.此方法虽比较灵活,可显示较多的汉字,但仍然局限于只能显示固定文件内容,且字模制取同样麻烦.

    一种较新的单片机"汉字动态编码与显示方案"(见《单片机与嵌入式系统应用》杂志2003年第1期和第9期),实际上也是一种动态的"小字库"法,只是字库的制取,索引的编写及文件的改码皆由PC机自动完成,免去了繁琐的人工处理.

    由上可见,目前单片机各种汉字显示方案均不理想.标准字库法,单片机不堪重负;而其它方法最大且又无法克服的缺点是,所显示文字皆有局限.显示内容也皆须专业人员设计而定,用户难于更改.这便极大地限制了单片机在各个领域的开拓和应用.究其原因,皆为单片机本身无汉卡,而这也正是我们致力于"51汉卡"开发的初衷.

    2.13×14点阵汉字字模

    为垫定"5l汉卡"的字型基础,首先开发成了l3×14点阵汉字字模.在目前通用的汉字字模中,最简单的是16×16点阵字模.在微型打字机中,也偶见有12×12点阵字模,但实用中不多见.字模点阵数直接决定着每一汉字所占单元内存值,能否在保证字模准确、美观的基础上,寻找一种较少的点阵字模呢?这便是我们最初的想法.于是我们经过反复选择比较,终于在国内首个推出了13×14点阵字模.此设计,一是基于我国汉字为方块字,故其行、列值需相近;二是汉字多有对称1生,故其列值宜奇不宜偶.设计实际表明,若行、列值很少,则难保证字模的准确性和美观性.?

    13×14点阵字模,是以我国现行简化字为准,并在此基础上设计而成.与目前通用的汉字16×l6点阵字模相比,其准确性和美观性并不逊色.然而其单字所占内存却由32个单元降至26个单元;另外使得每个单字显示由原来的256个像素降至l82个像素,使显示成本和空间均减少近三分之一.100×200点阵LED字屏,可显示16×l6点阵汉字72个,而l3×14点阵汉字便可显示l05个,且显示效果并无太大差异.这无疑对单片机和嵌入式系统汉字显示产品的开发和应用,具有明显的经济价值和实用意义.

    3.51单片机13×14点阵缩码汉卡

    "51汉卡"依据我国的汉字特点和单片机的快速构字功能,在13×14点阵字模基础上,以缩码形式开发而成单片机汉卡的开发,应以目前通用的主流单片机为研发对象,还应在囊括国标一、二级汉字及常用字符的前提下,使内存占用必须降至主流单片机可寻址范围内,且需留有足够的检字程序和用户应用空间.另外,字模设计必须准确、美观.字模提取速度也必须满足实用要求."51汉卡"的开发正是依据原则,并达到了以上各项要求.

    顾名思义,"51汉卡,即以MCS-51系列及其兼容单片机为研发对象.以51系列为代表的8位单片机,在过去、现在以及可以予见的将来,都将是嵌入式系统低端应用的主流机型.此乃业界专家的共识.

    "51汉卡"囊括了"GB2312-80"国标字库的全部一、二级汉字,并增补汉字86个;同时包括了大、小英文字母、阿拉伯数字等160个常用字符和不到4KB的构字程序,却仅总共占用了不足66KB的内存.每字平均约占9.8个单元,相对于16×16点阵每字占32单兀内存而言,尚不到其三分之一.这对于具有相互独立的64KB程序区和64KB数据区的51系列单片机而言,若适当配置内存,可为检字程序和用户留出90%以上的程序空间及相当数量的数据空间,对于一般用户的应用,都将绰绰有余.

    另外,为使"51汉卡''更便于使用和进一步节省内存,在上述基础上又开发成一套简化版本,删去了部分较偏僻的二级汉字.简化版本包括约5580个汉字,共占用内存58KB.实际上,按有关权威部门的统计,一般文本99%的文字是由2400个字写成的,因此使用简化版本,并配以简单的造字程序,一般亦可满足我们的使用要求.

    "51汉卡"所用字模,即我们开发的完全可与16×16点阵字模媲美的I3×14点阵汉字字模.字模提取速度是我们最为关心的问题之一.经测试及实际使用表明,"51汉卡''的提模速度完全可满足单片机汉字显示的实用要求.

    我们使用INTEL公司MCS-51经典系列87C51单片机在24MHz频率下测试,平均字模提取速度为2.1ms/字.因人的视觉暂留时间为0.1s,无论理论还是实际使用都表明,50字字模提取并显示,并无迟滞和待机之感.即使在1?2MHz频率下,20字取模,即点即出,在一般拼音检字和少量汉字显示中,完全可满足使用要求.随着单片机技术的迅速发展,目前,INTEL公司、Atmel公司、philips公司、我国台湾华邦等公司生产的MCS-51兼容单片机时钟频率可达33MHz,增强型可达40MHz,以至达60MHz;现市售的"STC89LE"系列单片机,最高频率可达90MHz.这些芯片都完全能与MCS-51芯片兼容,对于更高需求的场合,更新升级也十分简便.另外,在单片机和嵌入式系统中,文字显示速度要求并不高,只要满足换屏时的视觉要求即可.其汉字显示字数,一般也不太多.如用LCD显示屏,128×64点阵,才显示32个字;192×64点阵才显48个字;即使使用l3×14点阵字模,满屏也才56个汉字.

    4."51汉卡"设计依据及说明

    "51汉卡"设计依据是,我国汉字虽然数量繁多,字型各异,但其中复合结构者占大部分,并素有"偏旁取义,正字取音"之说.如"寸"字与不同偏旁可组成"村"、"付"、"讨"、"守"、"过"等字.因此"51汉卡"除单结构字基本以全码设计外,复台结构字多用相应的单体字及其偏旁,以结构代码写成.利用单片机快速的单元积木式构字程序,便可迅速生成字模代码.这既保证了提码速度,又节省了大量的汉卡内存.

    有关"51汉卡"的几点说明如下:

    ①凡汉字库中简、繁体字都有的用简体.如"後"以"后"代,"馀"以"余"代等;

    ②《新华字典》未收入字,多未收入,如"酏"、"鼽"等字,但"婧"、"弪"等字仍收入;

    ③对于多体字,一般以常用字代,如"摺"以"折"代,"镟"以"旋,代等,但"吒"不以"咤"代,"雠"不以"仇"代等;

    ④对通常已由其它字取代的字,都以这些字代替,如"岽"以"东"代,"肛''以"船"代等;

    ⑤二级汉字中,不单独构成汉字的偏旁未收入;

    ⑥依据名篇名著,生活用语等,增补汉字86个;

    ⑦收编大、小写英文字母、阿拉伯数字、标点符号等各种常用字符160个.

    5."51单片机汉卡"应用举例

    利用"51单片机汉卡",将使51系列单片机的汉字显示轻而易举,并可大为降低成本、体积和设计开发的难度,为单片机在生产控制、信息通信、文化教育和日常生活等领域,特别是计算机终端和手持产品的开发提供极大的便利和支持.?

    我们现已初步开发成"51汉卡"的"区位码输入法"和"拼音输入法,检字程序,并利用"51汉卡"成功地开发了带有廉价单片机控制器的LED汉字显示屏.这不仅大幅度降低了成本费用.而且用户可以通过单片机控制器,随心所欲地改变显示内容.

    51硬件设计

    CPU--87C51、12MHz晶振.

 

    程序存储器一1片EPROM?27C512.

    数据存储器一1片EPROM?27C512;1片EEPROM28C64A;1片6116.

    控制器显示屏一LCD?HY一19264B(深圳秋田视佳实业有限公司).

    LED屏选240×16点阵.

    本系统用标准小键盘检字,一次可予选4000字;控制器LCD满屏显示l3×14点阵汉字56个;LED屏满屏显示汉字19个.

    地址分配及用途如表l所列.

    5.2程序设计框图

    程序设计流程如图1所示.本系统采用12MHz晶振,若LCD取满屏56字,换屏时有约0.1s的延时,这对人的实际视觉并无大影响.

标准点阵汉字字库芯片

回到顶部

    1 概述

    GT23L24M1W是一款内含24X24点阵的汉字库芯片,支持GB18030国标汉字(含有国家信标委合法授

    权)及ASCII字符.排列格式为横置横排.用户通过字符内码,利用本手册提供的方法计算出该字符点阵

    在芯片中的地址,可从该地址连续读出字符点阵信息.

    1.1 芯片特点

    ●  数据总线: SPI 串行总线接口

    PLII 精简地址并行总线接口

    ●  点阵排列方式:字节横置横排

    访问速度:SPI 时钟频率:20MHz(max.)

    PLII 访问速度:130ns(max.) @3.3V

    ●  工作电压:2.7V~3.6V

    ●  电流:工作电流:12mA

    待机电流:10uA

    ●  封装:SO20W

    ●  尺寸(SO20W):12.80mmX10.30mm

    ●  工作温度:-20℃~85℃(SPI 模式下);-10℃~85℃(PLII 模式下)

    1.2 字库内容

    字型样张

    2 引脚描述与接口连接

    2.1 引脚名称

    2.2 SPI 接口引脚描述

    串行数据输出(SO):该信号用来把数据从芯片串行输出,数据在时钟的下降沿移出.

    串行数据输入(SI):该信号用来把数据从串行输入芯片,数据在时钟的上升沿移入.

    串行时钟输入(SCLK):数据在时钟上升沿移入,在下降沿移出.

    片选输入(CS#):所有串行数据传输开始于CE#下降沿,CE#在传输期间必须保持为低电平,在两条

    指令之间保持为高电平.

    总线挂起输入(HOLD#):

    2.3 SPI 接口与主机接口电路示意图

    SPI 与主机接口电路连接可以参考下图(#HOLD管脚建议接 2K 电阻 3.3V 拉高).

    若是采用系统电压为 5V的,则需要进行电平转换匹配连接 GT23 芯片,可以参考下图(#HOLD 管脚建议接 2K 电阻 3.3V 拉高).

    2.4 PLII 接口引脚描述

    2.5 PLII 接口与主机接口电路示意图

    SPI/PLII_SEL(管脚内部有 100K 上拉电阻)接地,字库芯片选择 PLII 接口模式,与主机接口电路连接可以参考下图.

    2.6 PLII 总线接口寻址说明

    在 PLII 总线模式下,芯片内部有 3个地址寄存器,主机需要把要读取数据的地址写入这 3个地址寄存器,然后再从数据寄存器中读出数据.主机每读一次数据寄存器,芯片内部的地址寄存器会自动增一,从而使主机只写一次首地址,就可以连续读取数据.

    3 字库调用方法

    3.1 汉字点阵排列格式

    每个汉字在芯片中是以汉字点阵字模的形式存储的,每个点用一个二进制位表示,存 1的点,当显示

    时可以在屏幕上显示亮点,存 0的点,则在屏幕上不显示.点阵排列格式为横置横排:即一个字节的高位

    表示左面的点,低位表示右面的点(如果用户按 word mode读取点阵数据,请注意高低字节的顺序),排

    满一行的点后再排下一行.这样把点阵信息用来直接在显示器上按上述规则显示,则将出现对应的汉字.

    3.1.1  24X24点汉字排列格式

    24X24 点汉字的信息需要 72个字节(BYTE 0 – BYTE 71)来表示.该 24X24 点汉字的点阵数据是

    横置横排的,其具体排列结构如下图:

    命名规则:

    最大字符集及字数

    S:GB2312 6,763汉字

    M:GB18030 27,484汉字

    T:GB12345  6,866汉字

    BIG5  5,401 / 13,060汉字

    U:Unicode V3.0  27,484汉字

posted @ 2011-03-18 15:01 wrh 阅读(4964) | 评论 (0)编辑 收藏

在c语言里可以用system()系统调用来处理

:批处理 
ShellExecute(null, "open ", 
        "c:\\abc.bat ", " ", " ",SW_SHOW   ); 


      


深入浅出ShellExecute   
译者:徐景周(原作:Nishant   S) 

Q:   如何打开一个应用程序?   ShellExecute(this-> m_hWnd, "open ", "calc.exe ", " ", " ",   SW_SHOW   ); 
或   ShellExecute(this-> m_hWnd, "open ", "notepad.exe ", 
        "c:\\MyLog.log ", " ",SW_SHOW   ); 
正如您所看到的,我并没有传递程序的完整路径。 
Q:   如何打开一个同系统程序相关连的文档?   ShellExecute(this-> m_hWnd, "open ", 
        "c:\\abc.txt ", " ", " ",SW_SHOW   ); 
Q:   如何打开一个网页?   ShellExecute(this-> m_hWnd, "open ", 
        "http://www.google.com ", " ", " ",   SW_SHOW   ); 
Q:   如何激活相关程序,发送EMAIL?   ShellExecute(this-> m_hWnd, "open ", 
        "mailto:nishinapp@yahoo.com ", " ", " ",   SW_SHOW   ); 
Q:   如何用系统打印机打印文档?   ShellExecute(this-> m_hWnd, "print ", 
        "c:\\abc.txt ", " ", " ",   SW_HIDE); 
Q:   如何用系统查找功能来查找指定文件?   ShellExecute(m_hWnd, "find ", "d:\\nish ", 
        NULL,NULL,SW_SHOW); 
Q:   如何启动一个程序,直到它运行结束?   SHELLEXECUTEINFO   ShExecInfo   =   {0}; 
ShExecInfo.cbSize   =   sizeof(SHELLEXECUTEINFO); 
ShExecInfo.fMask   =   SEE_MASK_NOCLOSEPROCESS; 
ShExecInfo.hwnd   =   NULL; 
ShExecInfo.lpVerb   =   NULL; 
ShExecInfo.lpFile   =   "c:\\MyProgram.exe ";
ShExecInfo.lpParameters   =   " ";
ShExecInfo.lpDirectory   =   NULL; 
ShExecInfo.nShow   =   SW_SHOW; 
ShExecInfo.hInstApp   =   NULL;
ShellExecuteEx(&ShExecInfo); 
WaitForSingleObject(ShExecInfo.hProcess,INFINITE); 
或:   PROCESS_INFORMATION   ProcessInfo;   
STARTUPINFO   StartupInfo;   //This   is   an   [in]   parameter 
ZeroMemory(&StartupInfo,   sizeof(StartupInfo)); 
StartupInfo.cb   =   sizeof   StartupInfo   ;   //Only   compulsory   field 
if(CreateProcess( "c:\\winnt\\notepad.exe ",   NULL,   
        NULL,NULL,FALSE,0,NULL, 
        NULL,&StartupInfo,&ProcessInfo)) 
{   
        WaitForSingleObject(ProcessInfo.hProcess,INFINITE); 
        CloseHandle(ProcessInfo.hThread); 
        CloseHandle(ProcessInfo.hProcess); 
}     
else 
{ 
        MessageBox( "The   process   could   not   be   started... "); 
} 

Q:   如何显示文件或文件夹的属性?   SHELLEXECUTEINFO   ShExecInfo   ={0}; 
ShExecInfo.cbSize   =   sizeof(SHELLEXECUTEINFO); 
ShExecInfo.fMask   =   SEE_MASK_INVOKEIDLIST   ; 
ShExecInfo.hwnd   =   NULL; 
ShExecInfo.lpVerb   =   "properties "; 
ShExecInfo.lpFile   =   "c:\\ ";   //can   be   a   file   as   well 
ShExecInfo.lpParameters   =   " ";   
ShExecInfo.lpDirectory   =   NULL; 
ShExecInfo.nShow   =   SW_SHOW; 
ShExecInfo.hInstApp   =   NULL;   
ShellExecuteEx(&ShExecInfo); 
posted @ 2011-03-09 13:42 wrh 阅读(3417) | 评论 (3)编辑 收藏

在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲技术。

    因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是我们就看到了闪烁现象。

    我们会很自然的想到,避免背景色的填充是最直接的办法。但是那样的话,窗体上会变的一团糟。因为每次绘制图象的时候都没有将原来的图象清除,造成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。所以单纯的禁止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快,于是我们想到了使用BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。以上也就是双缓冲绘图的基本的思路。

    一、普通方法:

    先按普通做图的方法进行编程。即在视类的OnDraw函数中添加绘图代码。在此我们绘制若干同心圆,代码如下:
 CBCDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

CPoint ptCenter;

CRect rect,ellipseRect;

GetClientRect(&rect);

ptCenter = rect.CenterPoint();

for(int i=20;i>0;i--)

{

ellipseRect.SetRect(ptCenter,ptCenter);

ellipseRect.InflateRect(i*10,i*10);

pDC->Ellipse(ellipseRect);

}


    编译运行程序,尝试改变窗口大小,可以发现闪烁现象。

    二、双缓冲方法:

    在双缓冲方法中,首先要做的是屏蔽背景刷新。背景刷新其实是在响应WM_ERASEBKGND消息。我们在视类中添加对这个消息的响应,可以看到缺省的代码如下:
 BOOL CMYView::OnEraseBkgnd(CDC* pDC)

{

return CView::OnEraseBkgnd(pDC);

}


    是调用父类的OnEraseBkgnd函数,我们屏蔽此调用,只须直接return TRUE;即可。

    下面是内存缓冲作图的步骤。
 CPoint ptCenter;

CRect rect,ellipseRect;

GetClientRect(&rect);

ptCenter = rect.CenterPoint();

CDC dcMem; //用于缓冲作图的内存DC

CBitmap bmp; //内存中承载临时图象的位图

dcMem.CreateCompatibleDC(pDC); //依附窗口DC创建兼容内存DC

bmp.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//创建兼容位图

dcMem.SelectObject(&bmp); //将位图选择进内存DC

//按原来背景填充客户区,不然会是黑色

dcMem.FillSolidRect(rect,pDC->GetBkColor());

for(int i=20;i>0;i--) //在内存DC上做同样的同心圆图象

{

ellipseRect.SetRect(ptCenter,ptCenter);

ellipseRect.InflateRect(i*10,i*10);

dcMem.Ellipse(ellipseRect);

}

pDC->BitBlt(0,0,rect.Width(),rect.Height(),

&dcMem,0,0,SRCCOPY);//将内存DC上的图象拷贝到前台

dcMem.DeleteDC(); //删除DC

bm.DeleteObject(); //删除位图


    由于复杂的画图操作转入后台,我们看到的是速度很快的复制操作,自然也就消除了闪烁现象。
    注意:bmp.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());

    这里面CreateCompatibleBitmap第一个参数不能用dcMem,这样的话创建的是黑白位图。如果你要创建彩色位图,需要用pDC,它用来创建了内存DC. 详细请见下面的MSDN:
 When a memory device context is created, it initially has a 1-by-1 monochrome bitmap selected into it. If this memory device context is used in CreateCompatibleBitmap, the bitmap that is created is a monochrome bitmap. To create a color bitmap, use the hDC that was used to create the memory device context, as shown in the following code:

HDC memDC = CreateCompatibleDC ( hDC );

HBITMAP memBM = CreateCompatibleBitmap ( hDC, nWidth, nHeight );

SelectObject ( memDC, memBM );





双缓冲技术绘图

  当数据量很大时,绘图可能需要几秒钟甚至更长的时间,而且有时还会出现闪烁现象,为了解决这些问题,可采用双缓冲技术来绘图。
  双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。双缓冲实现过程如下:
  1、在内存中创建与画布一致的缓冲区
  2、在缓冲区画图
  3、将缓冲区位图拷贝到当前画布上
  4、释放内存缓冲区
  在图形图象处理编程过程中,双缓冲是一种基本的技术。我们知道,如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲技术。因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是我们就看到了闪烁现象。 
  我们会很自然的想到,避免背景色的填充是最直接的办法。但是那样的话,窗体上会变的一团糟。因为每次绘制图象的时候都没有将原来的图象清除,造 成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。所以单纯的禁止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快,于是我们想到了使用 BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪 烁。以上也就是双缓冲绘图的基本的思路。 
  首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中: 
  CDC MemDC; //首先定义一个显示设备对象 
  CBitmap MemBitmap;//定义一个位图对象 //随后建立与屏幕显示兼容的内存显示设备 MemDC.CreateCompatibleDC(NULL); //这时还不能绘图,因为没有地方画 ^_^ 
  //下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小,也可以自己定义
  (如:有滚动条时就要大于当前窗口的大小,在BitBlt时决定拷贝内存的哪部分到屏幕上)
  MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight); //将位图选入到内存显示设备中 //只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上 
  CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap); //先用背景色将位图清除干净,这里我用的是白色作为背景 //你也可以用自己应该用的颜色 
  MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255)); //绘图 
  MemDC.MoveTo(……); MemDC.LineTo(……); //将内存中的图拷贝到屏幕上进行显示 
  pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY); //绘图完成后的清理 //把前面的pOldBit选回来.在删除MemBitmap之前要先从设备中移除它 
  MemDC.SelectObject(pOldBit); MemBitmap.DeleteObject(); MemDC.DeleteDC(); 双缓冲(two way soft-closing)
posted @ 2011-03-01 16:09 wrh 阅读(6539) | 评论 (1)编辑 收藏

4 画图
在Windows中,绘图一般在视图窗口的客户区进行,使用的是设备上下文类CDC中各种绘图函数。
1. 映射模式与坐标系
1)默认映射模式
映射模式(map mode)影响所有的图形和文本绘制函数,它定义(将逻辑单位转换为设备单位所使用的)度量单位和坐标方向,Windows总是用逻辑单位来绘图。
缺省情况下,绘图的默认映射模式为MM_TEXT,其绘图单位为像素(只要不打印输出,屏幕绘图使用该模式就够了)。若窗口客户区的宽和高分别为w和h像素,则其x坐标是从左到右,范围为0 ~ w-1;y坐标是从上到下,范围为0 ~ h-1。

2)设置映射模式
可以使用CDC类的成员函数GetMapMode和SetMapMode来获得和设置当前的映射模式:
int GetMapMode( ) const; // 返回当前的映射模式
virtual int SetMapMode( int nMapMode ); // 返回先前的映射模式


映射模式的nMapMode取值与含义
符号常量 数字常量 x方向 y方向 逻辑单位的大小 
MM_TEXT 1 向右 向下 像素 
MM_LOMETRIC 2 向右 向上 0.1 mm 
MM_HIMETRIC 3 向右 向上 0.01 mm 
MM_LOENGLISH 4 向右 向上 0.01 in 
MM_HIENGLISH 5 向右 向上 0.001 in 
MM_TWIPS 6 向右 向上 1/1440 in 
MM_ISOTROPIC 7 自定义 自定义 自定义 
MM_ANISOTROPIC 8 自定义 自定义 自定义


可见,除了两种自定义映射模式外,x方向都是向右,y方向也只有MM_TEXT的向下,其余的都是向上,与数学上一致。除了MM_ANISOTROPIC外,其他所有映射模式的x与y方向的单位都是相同的。所有映射模式的逻辑坐标的原点(0, 0)最初都是在窗口的左上角,但在CScrollView的派生类中,MFC会随用户滚动文档而自动调整逻辑原点的相对位置(改变视点的原点属性)。
3)自定义映射模式
自定义映射模式MM_ISOTROPIC(各向同性,x与y方向的单位必须相同)和MM_ANISOTROPIC(各向异性,x与y方向的单位可以不同)的单位和方向,可以通过用CDC类的成员函数G/SetWindowExt和G/SetViewportExt来获取/设置窗口和视口的大小来确定:
CSize GetWindowExt( ) const;
virtual CSize SetWindowExt( int cx, int cy );

virtual CSize SetWindowExt( SIZE size );
CSize GetViewportExt( ) const;
virtual CSize SetViewportExt( int cx, int cy );

virtual CSize SetViewportExt( SIZE size );
其中,cx或size.cx和cy或size.cy分别为窗口/视口的宽度与高度(逻辑单位)。
还可以用CDC类的成员函数SetViewportOrg来设置坐标原点的位置:
virtual CPoint SetViewportOrg( int x, int y );
CPoint SetViewportOrg( POINT point );
例如
void CDrawView::OnDraw(CDC* pDC) {
       CRect rect;
       GetClientRect(rect);
       pDC->SetMapMode(MM_ANISOTROPIC);
       pDC->SetWindowExt(1000,1000);
       pDC->SetViewportExt(rect.right, -rect.bottom);

       pDC->SetViewportOrg(rect.right / 2, rect.bottom /2);

       pDC->Ellipse(CRect(-500, -500, 500, 500));

}
将当前的映射模式设置为各向异性自定义映射模式,窗口大小为1000个逻辑单位宽和1000个逻辑单位高,视口大小同当前客户区,视口的坐标原点设置在当前客户区的中央。由于使用了负数作为SetViewportExt函数的第2个参数,所以y轴方向是向上的。

可见,圆被画成了椭圆,x与y方向上的逻辑单位不相同。
4)单位转换
对所有非MM_TEXT映射模式,有如下重要规则:
<!--[if !supportLists]-->l         <!--[endif]-->CDC的成员函数(如各种绘图函数)具有逻辑坐标参数

<!--[if !supportLists]-->l         <!--[endif]-->CWnd的成员函数(如各种响应函数)具有设备坐标参数(如鼠标位置point)

<!--[if !supportLists]-->l         <!--[endif]-->位置的测试操作(如CRect的PtInRect函数)只有使用设备坐标时才有效

<!--[if !supportLists]-->l         <!--[endif]-->长期使用的值应该用逻辑坐标保存(如窗口滚动后保存的设备坐标就无效了)


因此,为了使应用程序能够正确工作,除MM_TEXT映射模式外,其他映射模式都需要进行单位转换。下面是逻辑单位到设备单位(如像素)的转换公式:
x比例因子 = 视口宽度 / 窗口宽度
y比例因子 = 视口高度 / 窗口高度
设备x = 逻辑x * x比例因子 + x原点偏移量
设备y = 逻辑y * y比例因子 + y原点偏移量
Windows的GDI负责逻辑坐标和设备坐标之间的转换,这可以调用CDC类的成员函数LPtoDP和DPtoLP来进行:
void LPtoDP( LPPOINT lpPoints, int nCount = 1 ) const;

void LPtoDP( LPRECT lpRect ) const;
void LPtoDP( LPSIZE lpSize ) const;
void DPtoLP( LPPOINT lpPoints, int nCount = 1 ) const;

void DPtoLP( LPRECT lpRect ) const;
void DPtoLP( LPSIZE lpSize ) const;
例如:
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) {

              CRect rect = m_rect; // 逻辑坐标

              CClientDC dc(this);
              dc.SetMapMode(MM_LOENGLISH);
              dc.LPtoDP(rect); // 转化成设备坐标
       if (rect.PtInRect(point)) // 位置的测试操作只有使用设备坐标时才有效

       ......
}
void CDrawView:: OnMouseMove (UINT nFlags, CPoint point) {

       float t,y;

              char buf[40];
       CDC* pDC = GetDC();

pDC->SetMapMode(MM_HIMETRIC);
              pDC->DPtoLP(&point); // 转化成逻辑坐标
t = t1 + (point.x * dt) / w; sprintf(buf, "%.4fs", t); pSB->SetPaneText(xV, buf);

y = (y0 - point.y) / dy; sprintf(buf, "%.4f", y); pSB->SetPaneText(yV, buf);
       ......
}
2. 画像素点
画像素点就是设置像素点的颜色,从前面3)(2)已知道这可由CDC的成员函数SetPixel来做,该函数的原型为:
COLORREF SetPixel( int x, int y, COLORREF crColor ); 或

COLORREF SetPixel( POINT point, COLORREF crColor );

其中,x与y分别为像素点的横坐标与纵坐标,crColor为像素的颜色值。例如
pDC->SetPixel(i, j, RGB(r, g, b));

3.画线状图
在Windows中,线状图必须用笔来画(笔的创建与使用见前面的3)(3)),下面是CDC类中可以绘制线状图的常用成员函数:
<!--[if !supportLists]-->l         <!--[endif]-->当前位置:设置当前位置为(x, y)或point:(返回值为原当前位置的坐标)

CPoint MoveTo( int x, int y ); 或 CPoint MoveTo( POINT point );

<!--[if !supportLists]-->l         <!--[endif]-->画线:使用DC中的笔从当前位置画线到点(x, y)或point:(若成功返回非0值):

BOOL LineTo( int x, int y ); 或BOOL LineTo( POINT point );
<!--[if !supportLists]-->l         <!--[endif]-->画折线:使用DC中的笔,依次将点数组lpPoints中的nCount(≥2)个点连接起来,形成一条折线:

BOOL Polyline( LPPOINT lpPoints, int nCount );
<!--[if !supportLists]-->l         <!--[endif]-->画多边形:似画折线,但还会将最后的点与第一个点相连形成多边形,并用DC中的刷填充其内部区域:

BOOL Polygon( LPPOINT lpPoints, int nCount );
<!--[if !supportLists]-->l         <!--[endif]-->画矩形:使用DC中的笔画左上角为(x1, y1)、右下角为(x2, y2)或范围为*lpRect的矩形的边线,并用DC中的刷填充其内部区域:

BOOL Rectangle( int x1, int y1, int x2, int y2 ); 或
BOOL Rectangle( LPCRECT lpRect );
              有时需要根据用户给定的两个任意点来重新构造左上角和右下角的点,例如:
              rect = CRect(min(p0.x, point.x), min(p0.y, point.y), max(p0.x, point.x), max(p0.y, point.y));

<!--[if !supportLists]-->l         <!--[endif]-->画圆角矩形:使用DC中的笔画左上角为(x1, y1)、右下角为(x2, y2)或范围为*lpRect的矩形的边线,并用宽x3或point.x高y3或point.y矩形的内接椭圆倒角,再用DC中的刷填充其内部区域:

BOOL RoundRect( int x1, int y1, int x2, int y2, int x3, int y3 );
BOOL RoundRect( LPCRECT lpRect, POINT point );
例如:
int d = min(rect.Width(), rect.Height()) / 4;

pDC-> RoundRect(rect, CPoint(d, d));
<!--[if !supportLists]-->l         <!--[endif]-->画(椭)圆:使用DC中的笔在左上角为(x1, y1)、右下角为(x2, y2)或范围为*lpRect的矩形中画内接(椭)圆的边线,并用DC中的刷填充其内部区域:

BOOL Ellipse( int x1, int y1, int x2, int y2 );
BOOL Ellipse( LPCRECT lpRect );
注意,CDC中没有画圆的专用函数。在这里,圆是作为椭圆的(宽高相等)特例来画的。
<!--[if !supportLists]-->l         <!--[endif]-->画弧:(x1, y1)与(x2, y2)或lpRect的含义同画(椭)圆,(x3, y3)或ptStart为弧的起点,(x4, y4)或ptEnd为弧的终点:(逆时针方向旋转)

BOOL Arc( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );

BOOL Arc( LPCRECT lpRect, POINT ptStart, POINT ptEnd );
BOOL ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);

BOOL ArcTo(LPCRECT lpRect, POINT ptStart, POINT ptEnd);
画圆弧:(其中(x, y)为圆心、nRadius为半径、fStartAngle为起始角、fSweepAngle为弧段跨角)
BOOL AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle);

<!--[if !supportLists]-->l         <!--[endif]-->画弓弦:参数的含义同上,只是用一根弦连接弧的起点和终点,形成一个弓形,并用DC中的刷填充其内部区域:

BOOL Chord( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );

BOOL Chord( LPCRECT lpRect, POINT ptStart, POINT ptEnd );
4.画填充图
在Windows中,面状图必须用刷来填充(刷的创建与使用见前面的3)(4))。上面(2)中的Polygon、Rectangle、Ellipse和Chord等画闭合线状图的函数,只要DC中的刷不是空刷,都可以用来画对应的面状图(边线用当前笔画,内部用当前刷填充)。下面介绍的是CDC类中只能绘制面状图的其他常用成员函数:
<!--[if !supportLists]-->l         <!--[endif]-->画填充矩形:用指定的刷pBrush画一个以lpRect为区域的填充矩形,无边线,填充区域包括矩形的左边界和上边界,但不包括矩形的右边界和下边界:

void FillRect( LPCRECT lpRect, CBrush* pBrush );
<!--[if !supportLists]-->l         <!--[endif]-->画单色填充矩形:似FillRect,但只能填充单色,不能填充条纹和图案:

void FillSolidRect( LPCRECT lpRect, COLORREF clr );
void FillSolidRect( int x, int y, int cx, int cy, COLORREF clr );
<!--[if !supportLists]-->l         <!--[endif]-->画饼图(扇形):参数含义同Arc,但将起点和终点都与外接矩形的中心相连接,形成一个扇形区域,用DC中的刷填充整个扇形区域,无另外的边线:

BOOL Pie( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );

BOOL Pie( LPCRECT lpRect, POINT ptStart, POINT ptEnd );
<!--[if !supportLists]-->l         <!--[endif]-->画拖动的矩形:先擦除线宽为sizeLast、填充刷为pBrushLast的原矩形lpRectLast,然后再以线宽为size、填充刷为pBrush画新矩形lpRectLast。矩形的边框用灰色的点虚线画,缺省的填充刷为空刷:

void DrawDragRect( LPCRECT lpRect, SIZE size, LPCRECT lpRectLast,

SIZE sizeLast, CBrush* pBrush = NULL, CBrush* pBrushLast = NULL );

如:pDC->DrawDragRect(rect, size, rect0, size);

<!--[if !supportLists]-->l         <!--[endif]-->填充区域:

<!--[if !supportLists]-->n     <!--[endif]-->用当前刷从点(x, y)开始向四周填充到颜色为crColor的边界:

BOOL FloodFill(int x, int y, COLORREF crColor); // 成功返回非0

<!--[if !supportLists]-->n     <!--[endif]-->用当前刷从点(x, y)开始向四周填充:

BOOL ExtFloodFill(int x, int y, COLORREF crColor,

UINT nFillType); // 成功返回非0

<!--[if !supportLists]-->u <!--[endif]-->nFillType = FLOODFILLBORDER:填充到颜色为crColor的边界(同FloodFill);(用于填充内部颜色不同但边界颜色相同的区域)
<!--[if !supportLists]-->u <!--[endif]-->nFillType = FLOODFILLSURFACE:填充所有颜色为crColor的点,直到碰到非crColor颜色的点为止。(点(x, y)的颜色也必须为crColor),(用于填充内部颜色相同但边界颜色可以不同的区域)。例如:
pDC->ExtFloodFill(point.x, point.y, pDC->GetPixel(point), FLOODFILLSURFACE);

5.清屏
Windows没有提供专门的清屏函数,可以调用CWnd的下面两个函数调用来完成该功能:
void Invalidate(BOOL bErase = TRUE);
void UpdateWindow( );
或调用CWnd的函数
BOOL RedrawWindow(
   LPCRECT lpRectUpdate = NULL,

   CRgn* prgnUpdate = NULL,
   UINT flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE

);
来完成。
例如(菜单项ID_CLEAR的事件处理函数):
CDrawView::OnClear() { // 调用OnDraw来清屏
       //Invalidate();
       //UpdateWindow( );
       RedrawWindow( );
}
也可以用画填充背景色矩形的方法来清屏,如:
       RECT rect;
       GetClientRect(&rect);
       pDC->FillSolidRect(&rect, RGB(255, 255, 255));

6.在控件上绘图
可以在对话框资源中放置图片控件,并对其类型属性选Frame。可在对话框的绘图消息响应函数OnPaint或其他函数中,用CWnd类的函数GetDlgItem:
CWnd* GetDlgItem( int nID ) const;
来获得图片控件的窗口对象,再用函数GetDC:
CDC* GetDC( );

由窗口对象得到DC,然后就可以用该DC在控件中画图。如(在ID为IDC_HUESAT的图片控件上画调色板)
void CColorDlg::OnPaint() 
{
       if (IsIconic()) {
              ... ...
       }
       else {
              CDialog::OnPaint();
              int i, j;
              BYTE r, g, b;
              // get control window and DC of Hue&Saturation

              CWnd *pWin = GetDlgItem(IDC_HUESAT);

              CDC *pDC = pWin->GetDC();

              // draw hue-saturation palette

              for (i = 0; i < 360; i++)

                     for (j = 0; j <= 255; j++) {

                            HSLtoRGB(i, 255 - j, 128, r, g, b); // 自定义函数,见网络硬盘的

// res目录中的ColTrans.cpp文件
                            pDC->SetPixel(i, j, RGB(r, g, b));

                     }
              ... ...
       }
}
在非Frame类静态控件上绘图,必须先按顺序依次调用CWnd类的Invalidate和UpdateWindow函数后,再开始用DC画图。如在一个ID为IDC_COLOR的按钮上绘图:
void CComDlgDlg::DrawColor()
{
       CWnd* pWnd = GetDlgItem(IDC_COLOR);

       CDC* pDC = pWnd->GetDC();

       CRect rect;

       pWnd->GetClientRect(&rect);
       pWnd->Invalidate();
       pWnd->UpdateWindow();
       pDC->FillRect(&rect, new CBrush(m_crCol));
}

若干说明:
<!--[if !supportLists]-->l     <!--[endif]-->除了基于对话框的程序外,其他对话框类都需要自己添加(重写型)消息响应函数OnInitDialog,来做一些必要的初始化对话框的工作。添加方法是:先在项目区选中“类视图”页,再选中对应的对话框类,然后在属性窗口的“重写”页中添加该函数;

<!--[if !supportLists]-->l     <!--[endif]-->为了使在运行时能够不断及时更新控件的显示(主要是自己加的显式代码),可以将自己绘制控件的所有代码都全部加入对话框类的消息响应函数OnPaint中。在需要时(例如在绘图参数修改后),自己调用CWnd的Invalidate和UpdateWindow函数,请求系统刷新对话框和控件的显示。因为控件也是窗口,控件类都是CWnd的派生类。所以在对话框和控件中,可以像在视图类中一样,调用各种CWnd的成员函数。

<!--[if !supportLists]-->l     <!--[endif]-->一般的对话框类,缺省时都没有明写出OnPaint函数。可以自己在对话框类中添加WM_PAINT消息的响应函数OnPaint来进行一些绘图工作。

<!--[if !supportLists]-->l     <!--[endif]-->为了在鼠标指向按钮时,让按钮上自己绘制的图形不被消去,可以设置按钮控件的“Owner Draw”属性为“True”。

<!--[if !supportLists]-->l     <!--[endif]-->如果希望非按钮控件(如图片控件和静态文本等),也可以响应鼠标消息(如单击、双击等),需要设置控件的“Notify”属性为“True”。

<!--[if !supportLists]-->l     <!--[endif]-->使用OnPaint函数在对话框客户区的空白处(无控件的地方)绘制自己的图形,必须屏蔽掉其中缺省的对对话框基类的OnPaint函数的调用:

//CDialog::OnPaint();
<!--[if !supportLists]-->l     <!--[endif]-->对话框的背景色,可以用CWnd类的成员函数:

DWORD GetSysColor( int nIndex);
得到,其中的nIndex取为COLOR_BTNFACE。例如:
dc.SetBkColor(GetSysColor(COLOR_BTNFACE));

下面是部分例子代码:(其中FillColor和ShowImg为自定义的成员函数)
void CSetDlg::OnBnClickedPenColor()
{
       // TODO: 在此添加控件通知处理程序代码
       CColorDialog colDlg(m_crLineColor);

       if (colDlg.DoModal() == IDOK) {

              m_crLineColor = colDlg.GetColor();

              Invalidate();
              UpdateWindow();
       }
}
// ……
void CSetDlg::OnPaint()
{
       CPaintDC dc(this); // device context for painting

       // TODO: 在此处添加消息处理程序代码
       // 不为绘图消息调用 CDialog::OnPaint()
       FillColor(IDC_PEN_COLOR, m_crLineColor);

       FillColor(IDC_BRUSH_COLOR, m_crBrushColor);

       if(m_pBitmap0 != NULL) ShowImg(IDC_BRUSH_IMG, m_hBmp0);

       else if(m_pBitmap != NULL) ShowImg(IDC_BRUSH_IMG, m_hBmp);

}
void CSetDlg::FillColor(UINT id, COLORREF col)

{
       CWnd* pWnd = GetDlgItem(id);

       CDC* pDC = pWnd->GetDC();

       pDC->SelectObject(new CPen(PS_SOLID, 1, RGB(0, 0, 0)));

       pDC->SelectObject(new CBrush(col));

       CRect rect; 
       pWnd->GetClientRect(&rect);
       pWnd->Invalidate();
       pWnd->UpdateWindow();
       pDC->RoundRect(&rect, CPoint(8, 8));

}
void CSetDlg::ShowImg(UINT ID, HBITMAP hBmp)

{
       CWnd* pWnd = GetDlgItem(ID);

       CDC* pDC = pWnd->GetDC();

       CRect rect; 
       pWnd->GetClientRect(&rect);
       pWnd->Invalidate();
       pWnd->UpdateWindow();
       BITMAP bs;
       GetObject(hBmp, sizeof(bs), &bs);

       CDC dc;
       if(dc.CreateCompatibleDC(pDC)) {

              int x0, y0, w, h;
              float rx = (float)bs.bmWidth / rect.right,

                     ry = (float)bs.bmHeight / rect.bottom;

              if (rx >= ry) {

                     x0 = 0; w = rect.right;

                     h = (int)(bs.bmHeight / rx + 0.5);

                     y0 = (rect.bottom - h) / 2;

              }
              else {
                     y0 = 0; h = rect.bottom;

                     w = (int)(bs.bmWidth / ry + 0.5);

                     x0 = (rect.right - w) / 2;

              }
              ::SelectObject(dc.GetSafeHdc(), hBmp);

              pDC->SetStretchBltMode(HALFTONE);
              pDC->StretchBlt(x0, y0, w, h, &dc, 0, 0, bs.bmWidth, bs.bmHeight, SRCCOPY);

              SetDlgItemInt(IDC_W, bs.bmWidth);

              SetDlgItemInt(IDC_H, bs.bmHeight);

       }
}
//……
5 设置绘图属性
除了映射模式外,还有许多绘图属性可以设置,如背景、绘图方式、多边形填充方式、画弧方向、刷原点等。
1.背景
1)背景色
当背景模式为不透明时,背景色决定线状图的空隙颜色(如虚线中的空隙、条纹刷的空隙和文字的空隙),可以使用CDC类的成员函数GetBkColor和SetBkColor来获得和设置当前的背景颜色:
COLORREF GetBkColor( ) const; // 返回当前的背景色
virtual COLORREF SetBkColor( COLORREF crColor ); // 返回先前的背景色
                                                                              // 若出错返回0x80000000
2)背景模式
背景模式影响有空隙的线状图的空隙(如虚线中的空隙、条纹刷的空隙和文字的空隙)用什么办法填充。可以使用CDC类的成员函数GetBkMode和SetBkMode来获得和设置当前的背景模式:
int GetBkMode( ) const; // 返回当前背景模式
int SetBkMode( int nBkMode ); // 返回先前背景模式
背景模式的取值
nBkMode值 名称 作用 
OPAQUE 不透明的(缺省值) 空隙用背景色填充 
TRANSPARENT 透明的 空隙处保持原背景图不变

2. 绘图模式
绘图模式(drawing mode)指前景色的混合方式,它决定新画图的笔和刷的颜色(pbCol)如何与原有图的颜色(scCol)相结合而得到结果像素色(pixel)。
1)设置绘图模式
可使用CDC类的成员函数SetROP2 (ROP = Raster OPeration光栅操作)来设置绘图模式:
int SetROP2( int nDrawMode );

其中,nDrawMode可取值:
绘图模式nDrawMode的取值
符号常量 作用 运算结果 
R2_BLACK 黑色 pixel = black 
R2_WHITE 白色 pixel = white 
R2_NOP 不变 pixel = scCol 
R2_NOT 反色 pixel = ~scCol 
R2_COPYPEN 覆盖 pixel = pbCol 
R2_NOTCOPYPEN 反色覆盖 pixel = ~pbCol 
R2_MERGEPENNOT 反色或 pixel = ~scCol | pbCol 
R2_MERGENOTPEN 或反色 pixel = scCol | ~pbCol 
R2_MASKNOTPEN 与反色 pixel = scCol & ~pbCol 
R2_MERGEPEN 或 pixel = scCol | pbCol 
R2_NOTMERGEPEN 或非 pixel = ~(scCol | pbCol) 
R2_MASKPEN 与 pixel = scCol & pbCol 
R2_NOTMASKPEN 与非 pixel = ~(scCol & pbCol) 
R2_XORPEN 异或 pixel = scCol ^ pbCol 
R2_NOTXORPEN 异或非 pixel = ~(scCol ^ pbCol)

其中,R2_COPYPEN(覆盖)为缺省绘图模式,R2_XORPEN(异或)较常用。
2)画移动图形
为了能画移动的位置标识(如十字、一字)和随鼠标移动画动态图形(如直线、矩形、椭圆),必须在不破坏原有背景图形的基础上移动这些图形。
移动图形采用的是异或画图方法,移动图形的过程为:异或画图、在原位置再异或化图(擦除)、在新位置异或画图、……。

       pGrayPen = new CPen(PS_DOT, 0, RGB(128, 128, 128));

pDC->SetBkMode(TRANSPARENT);
       pOldPen = pDC->SelectObject(pGrayPen);
pDC->SelectStockObject(NULL_BRUSH);
       pDC->SetROP2(R2_XORPEN);
       if (m_bErase) pDC->Ellipse(rect0);
       pDC->Ellipse(rect);
       pDC->SetROP2(R2_COPYPEN);
       pDC->SelectObject(pOldPen);
       rect0 = rect;
较完整的拖放动态画图的例子,可参照下面的“3. 拖放画动态直线”部分。
3)其他属性
<!--[if !supportLists]-->l         <!--[endif]-->多边形填充方式:可使用CDC类的成员函数GetPolyFillMode和SetPolyFillMode来确定多边形的填充方式:

int GetPolyFillMode( ) const;
int SetPolyFillMode( int nPolyFillMode );
其中nPolyFillMode 可取值ALTERNATE(交替——填充奇数边和偶数边之间的区域,缺省值)或WINDING(缠绕——根据多边形边的走向来确定是否填充一区域)
<!--[if !supportLists]-->l         <!--[endif]-->画弧方向:可使用CDC类的成员函数GetArcDirection和SetArcDirection来确定Arc、Chord、Pie等函数的画弧方向:

int GetArcDirection( ) const;
int SetArcDirection( int nArcDirection );
其中,nArcDirection可取值AD_COUNTERCLOCKWISE(逆时针方向,缺省值)和AD_CLOCKWISE(顺时针方向)
<!--[if !supportLists]-->l         <!--[endif]-->刷原点:可使用CDC类的成员函数GetBrushOrg和SetBrushOrg来确定可填充绘图函数的条纹或图案刷的起点:(缺省值为客户区左上角的坐标原点(0, 0))

CPoint GetBrushOrg( ) const;
CPoint SetBrushOrg( int x, int y );
CPoint SetBrushOrg( POINT point );
3.拖放画动态直线
下面是一个较完整的拖放动态画直线的例子:
// 类变量
class CDrawView : public CView { 
       //……
protected:
       BOOL m_bLButtonDown, m_bErase; // 判断是否按下左鼠标键

//和是否需要擦除图形的类变量
       CPoint p0, pm; // 记录直线起点和动态终点的类变量

       CPen * pGrayPen, * pLinePen; // 定义灰色和直线笔

       //……
}
// 构造函数
CDrawView::CDrawView() {
       m_bLButtonDown = FALSE; // 设左鼠标键按下为假

       m_bErase = FALSE; // 设需要擦除为假

       pGrayPen = new CPen(PS_SOLID, 0, RGB(128, 128, 128));// 创建灰色笔

       pLinePen = new CPen(PS_SOLID, 0, RGB(255, 0, 0));// 创建红色的直线笔

}
// 鼠标消息响应函数
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) {

       m_bLButtonDown = TRUE; // 设左鼠标键按下为真

       SetCapture(); // 设置鼠标捕获

       // SetCursor(LoadCursor(NULL, IDC_CROSS)); // 设置鼠标为十字

       p0 = point; // 保存矩形左上角

       pm = p0; // 让矩形右下角等于左上角
       CView::OnLButtonDown(nFlags, point);

}
void CDrawView::OnMouseMove(UINT nFlags, CPoint point) {

       SetCursor(LoadCursor(NULL, IDC_CROSS)); // 设置鼠标为十字

       if (m_bLButtonDown) { // 左鼠标键按下为真

              CDC* pDC = GetDC(); // 获取设备上下文

              pDC->SelectObject(pGrayPen);// 选取灰色笔
              pDC->SetROP2(R2_XORPEN);// 设置为异或绘图方式
              if (m_bErase) { // 需要擦除为真

                     pDC->MoveTo(p0); pDC->LineTo(pm); // 擦除原直线

              }
              else // 需要擦除为假

                     m_bErase = TRUE; // 设需要擦除为真
              pDC->MoveTo(p0); pDC->LineTo(point); // 绘制新直线

              pm = point; // 记录老终点

              ReleaseDC(pDC); // 释放设备上下文

       }
       CView::OnMouseMove(nFlags, point);

}
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) {

       ReleaseCapture(); // 释放鼠标捕获

       if (m_bLButtonDown) { // 左鼠标键按下为真

              CDC* pDC = GetDC(); // 获取设备上下文

              pDC->SelectObject(pGrayPen);// 选取灰色笔
              pDC->SetROP2(R2_XORPEN); // 设置为异或绘图方式
              pDC->MoveTo(p0); pDC->LineTo(pm); // 擦除原直线

              pDC->SelectObject(pLinePen); // 选择直线笔
              pDC->SetROP2(R2_COPYPEN);// 设置为覆盖绘图方式
              pDC->MoveTo(p0); pDC->LineTo(point); // 绘制最终的直线

              m_bLButtonDown = FALSE; // 重设左鼠标键按下为假

              m_bErase = FALSE; // 重需要擦除为假

              ReleaseDC(pDC); // 释放设备上下文

       }
       CView::OnLButtonUp(nFlags, point);

}

posted @ 2011-03-01 14:03 wrh 阅读(2394) | 评论 (0)编辑 收藏
char是C语言标准数据类型,字符型,至于由几个字节组成通常由编译器决定,一般一个字节。

Windows为了消除各编译器的差别,重新定义了一些数据类型,你提到了另外几个类型都是这样。CHAR为

单字节字符。还有个WCHAR为Unicode字符,即不论中英文,每个字有两个字节组成。如果当前编译方式为

ANSI(默认)方式,TCHAR等价于CHAR,如果为Unicode方式,TCHAR等价于WCHAR。在当前版本LPCSTR和

LPSTR没区别,即以零结尾的字符串指针,相当于CHAR *。 LPSTR、LPCSTR相当于char *,所以这种类型

变量的赋值等同于char *的赋值
下面给出两个例子,一个是直接赋值,另一个是间接的。
    Ex1: LPSTR lpstrMsg = "I'm tired.";
    Ex2: char strMsg[]="I'm tired.";
    LPSTR lpstrMsg = (LPSTR) strMsg

posted @ 2011-02-17 09:14 wrh 阅读(544) | 评论 (0)编辑 收藏

[原创]在VC中彻底玩转Excel
    如今Excel是越来越重要了,在我们自己开发的程序中不免要和Excel打交道了。利用Automation技术,我们可以在不去了解
数据库的情况下玩转Excel,而且你会发现一切竟如此轻松!
    好了,咱们开始吧,我不喜欢用长篇累牍的代码来故弄玄虚,所以下面的代码都是切中要害的片段,总体上是个连贯的过程,
包括启动Excel,读取数据,写入数据,以及最后的关闭Excel,其中还包括了很多人感兴趣的合并单元格的处理。
    特别说明以下代码需要MFC的支持,而且工程中还要包含EXCEL2000的定义文件:EXCEL9.H,EXCEL9.CPP

*****************************************************************************************************************

 //*****
 //变量定义
 _Application app;   
 Workbooks books;
 _Workbook book;
 Worksheets sheets;
 _Worksheet sheet;
 Range range;
 Range iCell;
 LPDISPATCH lpDisp;   
 COleVariant vResult;
 COleVariant
        covTrue((short)TRUE),
        covFalse((short)FALSE),
        covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);   
 
 
 //*****
 //初始化COM的动态连接库
 if(!AfxOleInit()) 
 {
        AfxMessageBox("无法初始化COM的动态连接库!");
        return ;
     }  
 
 
 //*****
 //创建Excel 2000服务器(启动Excel)
 if(!app.CreateDispatch("Excel.Application"))
 {
  AfxMessageBox("无法启动Excel服务器!");
    return;
 }
 
 app.SetVisible(TRUE);          //使Excel可见
 app.SetUserControl(TRUE);      //允许其它用户控制Excel
 

 //*****  
 //打开c:\\1.xls
 books.AttachDispatch(app.GetWorkbooks());
 lpDisp = books.Open("C:\\\\1.xls",     
   covOptional, covOptional, covOptional, covOptional, covOptional,
   covOptional, covOptional, covOptional, covOptional, covOptional,
   covOptional, covOptional );   
 
    
 //*****
 //得到Workbook
 book.AttachDispatch(lpDisp);
 
 
 //*****
 //得到Worksheets
 sheets.AttachDispatch(book.GetWorksheets());
 
 
 //*****
 //得到当前活跃sheet
 //如果有单元格正处于编辑状态中,此操作不能返回,会一直等待
 lpDisp=book.GetActiveSheet();
 sheet.AttachDispatch(lpDisp);
 

 //*****
 //读取已经使用区域的信息,包括已经使用的行数、列数、起始行、起始列
 Range usedRange;
 usedRange.AttachDispatch(sheet.GetUsedRange());
 range.AttachDispatch(usedRange.GetRows());
 long iRowNum=range.GetCount();                   //已经使用的行数
 
 range.AttachDispatch(usedRange.GetColumns());
 long iColNum=range.GetCount();                   //已经使用的列数
 
 long iStartRow=usedRange.GetRow();               //已使用区域的起始行,从1开始
 long iStartCol=usedRange.GetColumn();            //已使用区域的起始列,从1开始
 
 
 //*****
 //读取第一个单元格的值
 range.AttachDispatch(sheet.GetCells());
 range.AttachDispatch(range.GetItem (COleVariant((long)1),COleVariant((long)1)).pdispVal );
 COleVariant vResult =range.GetValue();
 CString str;
 if(vResult.vt == VT_BSTR)       //字符串
 {
  str=vResult.bstrVal;
 }
 else if (vResult.vt==VT_R8)     //8字节的数字
 {
  str.Format("%f",vResult.dblVal);
 }
 else if(vResult.vt==VT_DATE)    //时间格式
 {
  SYSTEMTIME st;
     VariantTimeToSystemTime(&vResult.date, &st);
 }
 else if(vResult.vt==VT_EMPTY)   //单元格空的
 {
  str="";
 } 
 
 
 //*****
 //读取第一个单元格的对齐方式,数据类型:VT_I4
 //读取水平对齐方式
 range.AttachDispatch(sheet.GetCells());
 iCell.AttachDispatch((range.GetItem (COleVariant(long(1)), COleVariant(long(1)))).pdispVal);
 vResult.lVal=0;
 vResult=iCell.GetHorizontalAlignment();
 if(vResult.lVal!=0)
 {
  switch (vResult.lVal)
  {
  case 1:      //默认
   break;
  case -4108:  //居中
   break;
  case -4131 : //靠左
   break;
  case -4152 : //靠右
   break;
  }
 
 }
 
 //垂直对齐方式
 iCell.AttachDispatch((range.GetItem (COleVariant(long(1)), COleVariant(long(1)))).pdispVal);
 vResult.lVal=0;
 vResult=iCell.GetVerticalAlignment();
 if(vResult.lVal!=0)
 {
  switch (vResult.lVal)
  {
  case -4160 :  //靠上
   break;
  case -4108 :  //居中
   break;
  case -4107 :  //靠下
   break;
  }
 
 }
 
 
 //*****
 //设置第一个单元格的值"HI,EXCEL!"
 range.SetItem(COleVariant(1),COleVariant(1),COleVariant("HI,EXCEL!"));
 

 //*****
 //设置第一个单元格字体颜色:红色
 Font font;
 range.AttachDispatch(sheet.GetCells());
 range.AttachDispatch((range.GetItem (COleVariant(long(1)), COleVariant(long(1)))).pdispVal);
 font.SetColor(COleVariant((long)0xFF0000)); 
 
 
 //*****
 //合并单元格的处理
 //包括判断第一个单元格是否为合并单元格,以及将第一个单元格进行合并
 Range unionRange;
 range.AttachDispatch(sheet.GetCells());
 unionRange.AttachDispatch(range.GetItem (COleVariant((long)1),COleVariant((long)1)).pdispVal );
 
 vResult=unionRange.GetMergeCells();   
 if(vResult.boolVal==-1)             //是合并的单元格   
 {
  //合并单元格的行数
  range.AttachDispatch (unionRange.GetRows ());
  long iUnionRowNum=range.GetCount ();
  
  //合并单元格的列数
  range.AttachDispatch (unionRange.GetColumns ());
  long iUnionColumnNum=range.GetCount ();  
 
  //合并区域的起始行,列
  long iUnionStartRow=unionRange.GetRow();       //起始行,从1开始
  long iUnionStartCol=unionRange.GetColumn();    //起始列,从1开始
 
 }
 else if(vResult.boolVal==0)  
 {//不是合并的单元格}
 
 //将第一个单元格合并成2行,3列
 range.AttachDispatch(sheet.GetCells());
 unionRange.AttachDispatch(range.GetItem (COleVariant((long)1),COleVariant((long)1)).pdispVal );
 unionRange.AttachDispatch(unionRange.GetResize(COleVariant((long)2),COleVariant((long)3)));
 unionRange.Merge(COleVariant((long)0));   //合并单元格
 
 
 //*****
 //将文件保存为2.xls
 book.SaveAs(COleVariant("C:\\\\2.xls"),covOptional,covOptional, \\
  covOptional,covOptional,covOptional,0,\\
  covOptional,covOptional,covOptional,covOptional); 
 
 
 //*****
 //关闭所有的book,退出Excel
 book.Close (covOptional,COleVariant(OutFilename),covOptional);
 books.Close();     
 app.Quit();    

关于excel.h和excel.cpp,要注意版本问题.
比如对excel xp, 类库是直接包含在excel.exe中. 因此你只要用加入类(add class)的方法,直接选中excel.exe,并选择对话框中的常用的几个类(如Rang)就可以编程了. 千万不要选所有的类,否则太大了.

作者:unionsoft 2003-11-29 11:00:34)


修改字体颜色的那段漏了一句,应为:
  //*****
  //设置第一个单元格字体颜色:红色
  Font font;
  range.AttachDispatch(sheet.GetCells());
  range.AttachDispatch((range.GetItem (COleVariant(long(1)), COleVariant(long(1)))).pdispVal);
  font.AttachDispatch (range.GetFont ());
  font.SetColor(COleVariant((long)0xFF0000));

作者:zhengkuo 2003-12-2 10:50:00)


如果,程序既要安装在2000的计算机上,也可能安装在XP的机子上,有的用户还用97,(指的都是office),可能会出现版本问题------其中比较麻烦的是--在app.quit()后,仍旧存在excel进程,如果这样?怎么解决?
作者:zhengkuo 2003-12-2 10:51:50)


请教高手,能否用vb控制excel做成dll,在arx中进行调用,因为毕竟vb与excel亲切
作者:unionsoft 2004-1-30 11:06:58)

 

以下是引用zhengkuo在2003-12-2 10:50:00的发言:
如果,程序既要安装在2000的计算机上,也可能安装在XP的机子上,有的用户还用97,(指的都是office),可能会出现版本问题------其中比较麻烦的是--在app.quit()后,仍旧存在excel进程,如果这样?怎么解决?
兼容性问题:

        office2002-office97是向下兼容的,只要你不使用office2002中的新特性,程序在这些office版本中都好用

Excel程序不能退出的问题:
      1 . 不要使用#import导入类型库,如:#import "c:\\excel\\excel.olb"
      2 . 程序结束时,确保所有IDispatch都释放了,如:app.ReleaseDispatch (); 

作者:easypower 2004-5-14 10:00:39)


還是不明白如何得到excel.cpp和excel.h這兩個文件,請指教
作者:jack1975 2004-6-24 16:43:03)


有版本问题时,可以加一个判断:1、首先通过  exlApp得到版本,比如9.0,10.0,11.0,10.0以后的版本注意open函数的参数为15个,即最后在增加两个covOptional,即可,另外,补充一下,判断当前是否有excel应用程序在运行,使之更舒服一些:
::CLSIDFromProgID(L"Excel.Application",&clsid); // from registry
 if(::GetActiveObject(clsid, NULL,&pUnk) == S_OK)

{
  VERIFY(pUnk->QueryInterface(IID_IDispatch,(void**) &pDisp) == S_OK);
  ExcelApp.AttachDispatch(pDisp);
  pUnk->Release();
  }
else

 {
  if(!ExcelApp.CreateDispatch("Excel.Application")) {
   AfxMessageBox("Excel program not found");
   return 0;
  }

 }

 

作者:unionsoft 2004-12-13 12:36:09)

 

以下是引用zhmary在2004-12-10 11:30:10的发言:
请问各位高手,从哪里得onclick=Cswf() height=22 alt=Flash图片 src="skins/default/ubb/swf.gif" width=23 border=0>到Excel.cp...


Excel.cpp和Excel.h是从Excel的类型库中获取的,类型库类似C++中的头文件,包括接口,方法,属性的定义;类型库在Excel的安装目录可以找到,Excel的版本不同,这个类型库也不一样,如下所示:
Excel 95 and prior   :   xl5en32.olb
 Excel 97             :   excel8.olb
 Excel 2000           :   excel9.olb
 Excel 2002           :   excel.exe

具体的获取方法:

1 . 使用VC++新建立一个基于MFC的EXE工程

2 . 点击菜单"查看"-->"建立类向导",此时会弹全"MFC ClassWizard"对话框

3 . 点击"Add Class"-->"From a type libray",指定Excel的type libray,在Excel的安装目录下可以找到,如:"D:\\Microsoft Office\\Office\\EXCEL9.OLB"

4 . 在弹出的对话框中选择所需的类,按"确定",Excel.cpp和Excel.h就产生了。

作者:yfy2003 2004-12-23 17:41:57)


lpDisp = books.Open("C:\\\\1.xls",     
   covOptional, covOptional, covOptional, covOptional, covOptional,
   covOptional, covOptional, covOptional, covOptional, covOptional,
   covOptional, covOptional );   

编译显示错误:

error C2660: 'Open' : function does not take 13 parameters

作者:xux4618 2005-4-6 8:48:51)


请问怎样才能增加一个工作表?
作者:梦幻神话 2005-4-6 15:50:40)


 Workbooks.AttachDispatch(ExcelApp.GetWorkbooks());
 Workbook.AttachDispatch(Workbooks.Open(FileName,covOptional, covOptional,covOptional, covOptional,covOptional, covOptional,covOptional, covOptional,covOptional, covOptional,covOptional, covOptional));
 Worksheets.AttachDispatch(Workbook.GetWorksheets());
 Worksheet.AttachDispatch(Worksheets.GetItem((COleVariant((long)1))));
 Range.AttachDispatch(Worksheet.GetCells());
 iCell.AttachDispatch(Range.GetItem(COleVariant((long)2),COleVariant((long)2)).pdispVal);

 vResult = iCell.GetMergeCells();

 if(vResult.boolVal == -1)
 {
  AfxMessageBox("Yes");
 
  Range.AttachDispatch(iCell.GetRows());
  long row_num = Range.GetCount();

  Range.AttachDispatch(iCell.GetColumns());
  long col_num = Range.GetCount();

  CString str;
  str.Format("%d×%d",row_num,col_num);
  AfxMessageBox(str);
 }

请教:为什么str得到的结果都是1×1?(求合并单元格的原始行数和列数)。

谢谢。。。。。。。。。。

作者:unionsoft 2005-4-9 10:13:25)


首先你的Cells(2,2)是否处于合并单元格中
其次,你缺少了个关键语句:iCell.GetMergeArea()),你可以参考下面的语句

Range UnionRange;

UnionRange.AttachDispatch(iCell.GetMergeArea());  //先要获取合并区域

range.AttachDispatch (UnionRange.GetRows ());

long iRowNum = range.GetCount();             //合并单元格行数

range.AttachDispatch (UnionRange.GetColumns ());

long iColNum = Range.GetCount();            //合并单元格列数

   

我就想在excel里 实现,第一行为标题(居中)
第二行为时间(左对齐),下面为9列数据.最后一行为签名(左对齐)
怎么办?  回复  更多评论
 
# re: [转]在VC中彻底玩转Excel
2006-11-26 17:24 | 好学者

修改字体颜色的那段漏了一句,应为:
//*****
//设置第一个单元格字体颜色:红色
Font font;
range.AttachDispatch(sheet.GetCells());
range.AttachDispatch((range.GetItem (COleVariant(long(1)), COleVariant(long(1)))).pdispVal);
font.AttachDispatch (range.GetFont ());
font.SetColor(COleVariant((long)0xFF0000));

这样改了还是不能用,Font是哪儿来的哦!  回复  更多评论
 
# re: [转]在VC中彻底玩转Excel
2007-10-19 15:11 | 唐特

Font 和 _Application,Workbooks等等这些东西一样, 是打开excel.exe(excel9.olb) 的时候添加的class  回复  更多评论
 
# re: [转]在VC中彻底玩转Excel
2008-01-16 11:19 | HU

请教一个问题,现在出现了OFFICE 2007,在2000中做的程序,在2007下无法启动EXCEL服务,就是CreateDispatch失败。
但是,运行的环境既有2007,也会有老版本的EXCEL,这种情况下,怎么办?怎么让程序更有通用性?谢谢!!!!  回复  更多评论
 
# re: [转]在VC中彻底玩转Excel
2008-01-25 10:55 | 想飞的星期

请问怎么知道我createdispatch出来的excel已经关闭了??我还想每次重用同一个excel进程,怎么办?  回复  更多评论
 
# re: [转]在VC中彻底玩转Excel
2008-03-28 14:56 | lamorak

你好,请教一下,我在做数据导入到excel中的程序,将3个文件的数据导入没有问题,我为了做测试,将这3个文件复制了44次,就是145个文件的数据,总共有6702314行,200M的数据量,转换到75M数据的时候,Excel就不给转了,就弹出Excel中“value,range,GetIDsOfNames”函数出错,Excel大小有限制吗?我这个差不多就是做9×3文件循环数据插入,第10个循环就错了。我每次转一个文件后都加了Sleep(2000),能帮忙解决一下吗?  回复  更多评论


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/vicozo/archive/2009/04/12/4067804.aspx
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
_Application   ExcelApp;    
  Workbooks   wbsMyBooks;    
  _Workbook   wbMyBook;    
  Worksheets   wssMysheets;    
  _Worksheet   wsMysheet;    
  Range   rgMyRge;    
  //创建Excel   2000服务器(启动Excel)    
   
  if   (!ExcelApp.CreateDispatch("Excel.Application",NULL))    
  {    
  AfxMessageBox("创建Excel服务失败!");    
  return;  
  }    
  //得到路径  
  char   cCurrentDir[255];  
  ::GetCurrentDirectory(255,   cCurrentDir);  
  CString   str   =   cCurrentDir;  
  int   iPos   =   str.ReverseFind('\\');  
  CString   str1   =   str.Left(iPos);  
  CString   strDirectory;  
  strDirectory   =   _tcsdup(str1   +   "\\MyTemplate.xlt");    
  //利用模板文件建立新文档    
  wbsMyBooks.AttachDispatch(ExcelApp.GetWorkbooks(),true);    
  wbMyBook.AttachDispatch(wbsMyBooks.Add(_variant_t(strDirectory)));    
  //得到Worksheets    
  wssMysheets.AttachDispatch(wbMyBook.GetWorksheets(),true);    
  //得到sheet1    
  wsMysheet.AttachDispatch(wssMysheets.GetItem(_variant_t("sheet1")),true);    
  //得到全部Cells,此时,rgMyRge是cells的集合    
  rgMyRge.AttachDispatch(wsMysheet.GetCells(),true);    
  //设置1行1列的单元的值                     行                         列  
   
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)1),_variant_t("名称"));    
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)2),_variant_t("时间"));  
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)3),_variant_t("信息"));  
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)4),_variant_t("级别"));  
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)5),_variant_t("确认时间"));  
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)6),_variant_t("人"));  
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)7),_variant_t("啊啊"));  
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)8),_variant_t("cc"));  
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)9),_variant_t("gh元"));  
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)10),_variant_t("h"));  
  rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)11),_variant_t("dsf"));  
   
   
   
  //int   j=m_theListCtrl.GetItemCount();  
  //CString   str;  
  //str=m_theListCtrl.GetItemText(0,1);  
  //MessageBox(str);  
   
   
  for(int   i   =   2;   i   <   m_theListCtrl.GetItemCount()+2;   i++)  
  {  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)1),_variant_t(m_theListCtrl.GetItemText(i-2,0)));  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)2),_variant_t(m_theListCtrl.GetItemText(i-2,1)));  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)3),_variant_t(m_theListCtrl.GetItemText(i-2,2)));  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)4),_variant_t(m_theListCtrl.GetItemText(i-2,3)));  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)5),_variant_t(m_theListCtrl.GetItemText(i-2,4)));  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)6),_variant_t(m_theListCtrl.GetItemText(i-2,5)));  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)7),_variant_t(m_theListCtrl.GetItemText(i-2,6)));  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)8),_variant_t(m_theListCtrl.GetItemText(i-2,7)));  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)9),_variant_t(m_theListCtrl.GetItemText(i-2,8)));  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)10),_variant_t(m_theListCtrl.GetItemText(i-2,9)));  
  rgMyRge.SetItem(_variant_t((long)i),_variant_t((long)11),_variant_t(m_theListCtrl.GetItemText(i-2,10)));  
   
  }  
   
  //得到所有的列    
  rgMyRge.AttachDispatch(wsMysheet.GetColumns(),true);    
   
   
  //得到第一列   //设置列宽    
  rgMyRge.AttachDispatch(rgMyRge.GetItem(_variant_t((long)1),vtMissing).pdispVal,true);    
  rgMyRge.SetColumnWidth(_variant_t((long)15));    
   
  rgMyRge.AttachDispatch(rgMyRge.GetItem(_variant_t((long)2),vtMissing).pdispVal,true);    
  rgMyRge.SetColumnWidth(_variant_t((long)15));    
   
  rgMyRge.AttachDispatch(rgMyRge.GetItem(_variant_t((long)3),vtMissing).pdispVal,true);    
  rgMyRge.SetColumnWidth(_variant_t((long)16));    
   
  rgMyRge.AttachDispatch(rgMyRge.GetItem(_variant_t((long)4),vtMissing).pdispVal,true);    
  rgMyRge.SetColumnWidth(_variant_t((long)15));    
   
  rgMyRge.AttachDispatch(rgMyRge.GetItem(_variant_t((long)5),vtMissing).pdispVal,true);    
  rgMyRge.SetColumnWidth(_variant_t((long)15));    
   
  //显示excel表  
  wbMyBook.SetSaved(true);    
  ExcelApp.SetVisible(true);    
  //wbMyBook.PrintPreview(_variant_t(false));    
   
  //释放对象    
  rgMyRge.ReleaseDispatch();    
  wsMysheet.ReleaseDispatch();    
  wssMysheets.ReleaseDispatch();    
  wbMyBook.ReleaseDispatch();    
  wbsMyBooks.ReleaseDispatch();    
  ExcelApp.ReleaseDispatch();   

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/vicozo/archive/2009/04/12/4067835.aspx
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SDK程序,没有用MFC。  
  用的是excel2003  
  //   StatisticsGen.cpp   :   Defines   the   entry   point   for   the   console   application.  
  //  
   
  #include   "stdafx.h"  
   
  int   _tmain(int   argc,   _TCHAR*   argv[])  
  {  
  // Dispatch   interface  
  IDispatch *pDispExcel;  
  // Temporary   variable   to   hold   names.  
  unsigned   short *ucPtr;  
  // ClSID   of   excel  
  CLSID clsidExcel;  
  // Return   value  
  HRESULT hr;  
   
   
  // Initialize   OLE   Libraries.  
  OleInitialize(NULL);  
   
   
  // Get   CLSID   for   Excel.Application   from   registry.  
  hr   =   CLSIDFromProgID(L"Excel.Application",   &clsidExcel);  
  if   (   FAILED(hr)   )  
  {  
  MessageBox(NULL,   "Excel   not   registered.",   "Error",   MB_OK);  
  goto   __exit;  
  }  
   
  ///////////////////////////////////////////////////////////////////////////////////////////////////  
  // Start   excel   and   get   its   IDispatch   pointer.  
  hr   =   CoCreateInstance(clsidExcel,   NULL,   CLSCTX_LOCAL_SERVER,   IID_IDispatch,   (void**)&pDispExcel);  
  if   (   FAILED(hr)   )  
  {  
  MessageBox(NULL,   "Couldn't   start   Excel.",   "Error",   MB_OK);  
  goto   __exit;  
  }  
   
  ///////////////////////////////////////////////////////////////////////////////////////////////////  
  // Get   the   'visible'   property's   DISPID.  
  DISPPARAMS dispParamsVisible   =   {   NULL,   NULL,   0,   0};  
  VARIANT parmVisible;  
  DISPID dispidNamed   =   DISPID_PROPERTYPUT;  
  DISPID dispVisible;  
   
  ucPtr   =   L"Visible";  
  pDispExcel->GetIDsOfNames(IID_NULL,   &ucPtr,   1,   LOCALE_USER_DEFAULT,   &dispVisible);  
   
  // Initialize   parameters   to   set   visible   property   to   true.  
  VariantInit(&parmVisible);  
  parmVisible.vt   =   VT_I4;  
  parmVisible.llVal   =   1;  
   
  //   One   argument.  
  dispParamsVisible.cArgs   =   1;  
  dispParamsVisible.rgvarg   =   &parmVisible;  
   
  // Handle   special-case   for   property-puts!  
  dispParamsVisible.cNamedArgs =   1;  
  dispParamsVisible.rgdispidNamedArgs =   &dispidNamed;  
   
  // Set   'visible'   property   to   true.  
  hr   =   pDispExcel->Invoke(dispVisible,   IID_NULL,   LOCALE_SYSTEM_DEFAULT,     DISPATCH_PROPERTYPUT   |   DISPATCH_METHOD,  
  &dispParamsVisible,   NULL,   NULL,   NULL);  
  VariantClear(&parmVisible);  
   
  if(FAILED(hr))    
  {  
  MessageBox(NULL,   "Set   visible!",   "Failed!",   MB_OK);  
  goto   __exit;  
  }  
   
   
  /////////////////////////////////////////////////////////////////////////////////////////////////////  
  OLECHAR*   szGetBooks   =   L"Workbooks";    
  DISPID dispGetWorkbooks;  
  VARIANT   varBooks;  
   
  hr   =   pDispExcel->GetIDsOfNames(IID_NULL,   &szGetBooks,   1,   LOCALE_USER_DEFAULT,   &dispGetWorkbooks);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
  DISPPARAMS   dispParamsForGetBooks   =   {   NULL,   NULL,   0,   0};  
  hr   =   pDispExcel->Invoke(dispGetWorkbooks,   IID_NULL,   LOCALE_SYSTEM_DEFAULT,     DISPATCH_PROPERTYGET   |   DISPATCH_METHOD,  
  &dispParamsForGetBooks,   &varBooks,   NULL,   NULL);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
   
  ///////////////////////////////////////////////////////////////////////////////////////////////////  
  // Open  
  IDispatch*   pDispBooks;  
  IDispatch*   pDispBook;  
   
  if   (   varBooks.vt   !=   VT_DISPATCH)  
  goto   __exit;  
  else  
  {  
  // Get   workbooks   dispatch   interface  
  pDispBooks   =   varBooks.pdispVal;  
   
  // Open  
  VARIANT   varRetBook;  
  VARIANTARG   varg;  
  varg.vt   =   VT_BSTR;  
  varg.bstrVal   =   _bstr_t("c:\\bool.xls");  
   
  DISPPARAMS   dpOpen=   {   &varg,   NULL,   1,   0   };  
   
  LPOLESTR   lpOpen   =   L"Open";  
  DISPID   dispOpen;  
   
  hr   =   pDispBooks->GetIDsOfNames(IID_NULL,   &lpOpen,   1,   LOCALE_USER_DEFAULT,   &dispOpen);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
  hr   =   pDispBooks->Invoke(dispOpen,   IID_NULL,   LOCALE_SYSTEM_DEFAULT,   DISPATCH_PROPERTYGET   |   DISPATCH_METHOD,  
  &dpOpen,   &varRetBook,   NULL,   NULL);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
   
  if   (   varRetBook.vt   !=   VT_DISPATCH   )  
  goto   __exit;  
  else  
  pDispBook   =   varRetBook.pdispVal;  
  }  
 

//   worksheets  
  IDispatch*   pDispSheets;  
  if   (   pDispBook   !=   NULL   )  
  {  
  DISPPARAMS   dpSheets   =   {NULL,   NULL,   0,   0};  
  DISPID dispSheets;  
  LPOLESTR   lpSheets   =   L"Worksheets";  
  VARIANT   varRetSheets;  
   
  hr   =   pDispBook->GetIDsOfNames(IID_NULL,   &lpSheets,   1,   LOCALE_USER_DEFAULT,   &dispSheets);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
  hr   =   pDispBook->Invoke(dispSheets,   IID_NULL,   LOCALE_SYSTEM_DEFAULT,   DISPATCH_PROPERTYGET   |   DISPATCH_METHOD,  
  &dpSheets,   &varRetSheets,   NULL,   NULL);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
   
  if   (   varRetSheets.vt   !=   VT_DISPATCH   )  
  goto   __exit;  
  else  
  pDispSheets   =   varRetSheets.pdispVal;  
  }  
   
  ///////////////////////////////////////////////////////////////////////////////////////////////////  
  // worksheet  
  IDispatch*   pDispSheet;  
  if   (   pDispSheets   !=   NULL   )  
  {  
  VARIANT     varRetSheet;  
   
  VARIANTARG   vargSheet;  
  vargSheet.vt   =   VT_I4;  
  vargSheet.intVal   =   1;  
   
  DISPPARAMS dpSheet   =   {   &vargSheet,   NULL,   1,   0   };  
   
  LPOLESTR   lpSheet   =   L"Item";  
  DISPID dispSheet;  
   
  hr   =   pDispSheets->GetIDsOfNames(IID_NULL,   &lpSheet,   1,   LOCALE_USER_DEFAULT,   &dispSheet);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
  hr   =   pDispSheets->Invoke(dispSheet,   IID_NULL,   LOCALE_USER_DEFAULT,   DISPATCH_PROPERTYGET   |   DISPATCH_METHOD,    
  &dpSheet,   &varRetSheet,   NULL,   NULL);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
   
  if   (   varRetSheet.vt   !=   VT_DISPATCH   )  
  goto   __exit;  
  else  
  pDispSheet   =   varRetSheet.pdispVal;  
  }  
   
  ///////////////////////////////////////////////////////////////////////////////////////////////////  
  IDispatch*   pDispRange;  
  if   (   pDispSheet   !=   NULL   )  
  {  
  LPOLESTR   lpCells   =   L"Cells";  
  DISPPARAMS   dpCells   =   {   NULL,   NULL,   0,   0   };  
  DISPID   dispCells;  
  VARIANT   varRetRange;  
   
  hr   =   pDispSheet->GetIDsOfNames(IID_NULL,   &lpCells,   1,   LOCALE_USER_DEFAULT,   &dispCells);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
  hr   =   pDispSheet->Invoke(dispCells,   IID_NULL,   LOCALE_USER_DEFAULT,     DISPATCH_PROPERTYGET   |   DISPATCH_METHOD,  
  &dpCells,   &varRetRange,   NULL,   NULL);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
   
  if   (   varRetRange.vt   !=   VT_DISPATCH   )  
  goto   __exit;  
  else  
  pDispRange   =   varRetRange.pdispVal;  
  }  
   
  ///////////////////////////////////////////////////////////////////////////////////////////////////  
  // put   value   in   Item   property  
  if   (   pDispRange   !=   NULL   )  
  {  
  LPOLESTR   lpRangeItem   =   L"Item";  
  DISPID   dispRangeItem;  
   
  hr   =   pDispRange->GetIDsOfNames(IID_NULL,   &lpRangeItem,   1,   LOCALE_USER_DEFAULT,   &dispRangeItem);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
   
  VARIANT*   vargRangeItem   =   new   VARIANT[3];  
   
  for   (   int   i   =   0;   i   <   3;   i   ++   )  
  VariantInit(&vargRangeItem[i]);  
   
  vargRangeItem[0].vt   =   VT_I4;  
  vargRangeItem[0].intVal   =   1;  
  vargRangeItem[1].vt   =   VT_I4;  
  vargRangeItem[1].intVal   =   1;  
  vargRangeItem[2].vt   =   VT_I4;  
  vargRangeItem[2].intVal   =   1;  
   
  DISPPARAMS   dpRangeItem   =   {NULL,   NULL,   0,   0};  
  dpRangeItem.cArgs   =   3;  
  dpRangeItem.rgvarg   =   vargRangeItem;  
  dpRangeItem.cNamedArgs   =   1;  
  DISPID   dispIDRangeItem   =   DISPID_PROPERTYPUT;  
  dpRangeItem.rgdispidNamedArgs   =   &dispIDRangeItem;  
   
  EXCEPINFO   except;  
   
  hr   =   pDispRange->Invoke(dispRangeItem,   IID_NULL,   LOCALE_USER_DEFAULT,   DISPATCH_PROPERTYPUT   |   DISPATCH_METHOD,  
  &dpRangeItem,   NULL,   &except,   NULL);  
   
  delete   []vargRangeItem;  
   
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
  }  
   
  ///////////////////////////////////////////////////////////////////////////////////////////////////  
  // Save  
  if   (   pDispBook   )  
  {  
  LPOLESTR   lpBookSave   =   L"Save";  
  DISPID   dispIDBookSave;  
   
  hr   =   pDispBook->GetIDsOfNames(IID_NULL,   &lpBookSave,   1,   LOCALE_USER_DEFAULT,   &dispIDBookSave);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
  DISPPARAMS   dispBookSave   =   {   NULL,   NULL,   0,   0   };  
   
  hr   =   pDispBook->Invoke(dispIDBookSave,   IID_NULL,   LOCALE_USER_DEFAULT,   DISPATCH_METHOD,   &dispBookSave,   NULL,   NULL,   NULL);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
  }  
   
  ///////////////////////////////////////////////////////////////////////////////////////////////  
  // quit  
  if   (   pDispExcel   )  
  {  
  LPOLESTR   lpExcelQuit   =   L"Quit";  
  DISPID   dispIDExcelQuit;  
   
  hr   =   pDispExcel->GetIDsOfNames(IID_NULL,   &lpExcelQuit,   1,   LOCALE_USER_DEFAULT,   &dispIDExcelQuit);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
   
  DISPPARAMS   dispExcelQuit   =   {   NULL,   NULL,   0,   0   };  
   
  hr   =   pDispExcel->Invoke(dispIDExcelQuit,   IID_NULL,   LOCALE_USER_DEFAULT,   DISPATCH_METHOD,   &dispExcelQuit,  
  NULL,   NULL,   NULL);  
  if   (   FAILED(hr)   )  
  goto   __exit;  
   
  }  
   
  __exit:  
  if   (   pDispRange   )  
  pDispRange->Release();  
   
  if   (   pDispSheet   )  
  pDispSheet->Release();  
   
  if   (   pDispSheets   )  
  pDispSheets->Release();  
   
  if   (   pDispBook   )  
  pDispBook->Release();  
   
  if   (   pDispBooks   )  
  pDispBooks->Release();  
   
  if   (   pDispExcel   )  
  pDispExcel->Release();  
   
  OleUninitialize();  
   
  return   0;  
  } 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/vicozo/archive/2009/04/12/4067838.aspx

posted @ 2011-02-15 08:58 wrh 阅读(2373) | 评论 (0)编辑 收藏
仅列出标题
共25页: 1 2 3 4 5 6 7 8 9 Last 

导航

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

统计

常用链接

留言簿(19)

随笔档案

文章档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜