• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            先調(diào)用GetWindowRect后再調(diào)用ScreenToClient,這個(gè)時(shí)候得到的rect和直接使用GetClientRect得到的值是相等的。有時(shí)候需要獲得窗口矩形的大小和客戶區(qū)矩形的大小二者的值,故需要分別調(diào)用GetWindowRect和GetClientRect。如果只需要獲得客戶區(qū)矩形的大小,調(diào)用GetClientRect就行了。GetWindowRect和GetClientRect函數(shù)的說(shuō)明如下:

            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() 得到的是在屏幕坐標(biāo)系下的RECT;(即以屏幕左上角為原點(diǎn)
            GetClientRect() 得到的是在客戶區(qū)坐標(biāo)系下的RECT; (即以所在窗口左上角為原點(diǎn)

            GetWindowRect()取的是整個(gè)窗口的矩形;
            GetClientRect()取的僅是客戶區(qū)的矩形,也就是說(shuō)不包括標(biāo)題欄,外框等;

            第一個(gè)函數(shù)獲得的是窗口在屏幕上的位置,得到的結(jié)果可能是這樣CRect(10,10,240,240);
            第二個(gè)函數(shù)和它不同,它只獲得了客戶區(qū)的大小,因此得到的結(jié)果總是這樣CRect(0,0,width,height);

            ScreenToClient() 就是把屏幕坐標(biāo)系下的RECT坐標(biāo)轉(zhuǎn)換為客戶區(qū)坐標(biāo)系下的RECT坐標(biāo)。

             

            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得到的是客戶區(qū)的大小,也就是說(shuō)這樣得到的左上角永遠(yuǎn)是(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 是窗口相對(duì)于整個(gè)屏幕的坐標(biāo),屏幕左上點(diǎn)為0,0

            相互轉(zhuǎn)化用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.


            雖然存在調(diào)用GetWindowRect后再調(diào)用ScreenToClient==GetClientRect,但ScreenToClient()和ClientToScreen()兩者都是屬于WINDOWS API函數(shù),可能是存在一定的冗余設(shè)計(jì),但意義不同。
            不過(guò)在.Net Framework下對(duì)WINDOWS API函數(shù)進(jìn)行了重新整理和優(yōu)化,在獲取控件或窗口的屏幕坐標(biāo)和客戶區(qū)坐標(biāo)時(shí)更方便的多,只需要得到與控件或窗口相對(duì)應(yīng)屏幕坐標(biāo)和客戶區(qū)坐標(biāo)屬性值就可以了

            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 閱讀(967) | 評(píng)論 (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 閱讀(638) | 評(píng)論 (0)編輯 收藏

            各位經(jīng)驗(yàn)豐富的學(xué)者專家老師們:
                你們好!
                大家都知道大字體gbcbig.shp是AutoCAD本地化時(shí)需要的一個(gè)字體定義形文件,其轉(zhuǎn)化為gbcbig.shx后可被AutoCAD調(diào)用。現(xiàn)在我正在做一項(xiàng)工作,想將這種.SHX文件轉(zhuǎn)換成.aa文件,以擴(kuò)展.shx格式文件的使用范圍,說(shuō)明一下:.aa文件是ThinkDesign中的字體文件定義格式。我想實(shí)現(xiàn)他們?nèi)撸?shp,.shx,.aa)之間的相互轉(zhuǎn)化.目前已經(jīng)基本實(shí)現(xiàn)了這個(gè)目標(biāo),具體包括:shx-->shp  shp-->aa
            shx-->aa 。在實(shí)現(xiàn)將shp-->aa/shx-->aa 中,我需要知道.shp/.shx文件中字高的定義。
            在AutoCAD幫助文件中有如下解釋:
            *0,4,font-name
            above,below,modes,0
            其中,above 值指出大寫字母從基線向上延伸的矢量長(zhǎng)度,below 指出小寫字母從基線向下延伸的距離。基線的概念與紙面書寫時(shí)的基準(zhǔn)線相似。這些值定義了基本字符尺寸,用作 TEXT 命令中指定高度的縮放比例。
            舉個(gè)例子:
            *BIGFONT 7019,1,161,254
            *0,4,工程漢字字符集 1998.1. FAW/Autodesk CTC CBX(0293)
            0,64,2,0
            在實(shí)現(xiàn)將shp轉(zhuǎn)化aa和將shx轉(zhuǎn)化aa 中,我將字高規(guī)定為max(above,below),在gbcbig中具體表現(xiàn)為max(0,64),除了gbcbig這個(gè)字體定義外,其他都還算合適,問(wèn)題就出在gbcbig上,因?yàn)間bcbig中有一個(gè)子形*142,具體是這樣定義的:
            *142,14,起始描述(比例系數(shù)加入和起點(diǎn)調(diào)整)
            4,9,3,102,2,14,8,(-34,-80),2,8,(0,-5),0
            這就有一個(gè)問(wèn)題,如果我把max(0,64)當(dāng)成是字高的話,對(duì)于一個(gè)具體的字的定義來(lái)說(shuō),比如*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
            這個(gè)字在CAD中顯示出來(lái)就是一個(gè)正常顯示的9/102大小的字,非常的小,難以辨識(shí)。
            我現(xiàn)在需要解決這個(gè)字體縮放問(wèn)題。為什么這個(gè)gbcbig字體顯示結(jié)果會(huì)是這樣小呢,我懷疑是我把字高的定義搞錯(cuò)了,查了很多資料,也沒有答案,特請(qǐng)大家?guī)兔Α?br>                    
            問(wèn)題1:字體的形定義中字高是如何定義的
            問(wèn)題2:為什么一般的普通大字體(非擴(kuò)展大字體)文件,比如
            *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的定義部分前兩位卻是反過(guò)來(lái)的:0,64?是偶然還是另有其他解釋呢?







            一問(wèn):為什么不能顯示漢字?或輸入的漢字變成了問(wèn)號(hào)
            答:原因可能是:
            1. 對(duì)應(yīng)的字型沒有使用漢字字體,如HZTXT.SHX等;
            2. 當(dāng)前系統(tǒng)中沒有漢字字體形文件;應(yīng)將所用到的形文件復(fù)制AutoCAD的字體目錄中(一般為...\FONTS\);
            3. 對(duì)于某些符號(hào),如希臘字母等,同樣必須使用對(duì)應(yīng)的字體形文件,否則會(huì)顯示成?號(hào)。

            二問(wèn):為什么輸入的文字高度無(wú)法改變
            答:使用的字型的高度值不為0時(shí),
            DTEXT命令書寫文本時(shí)都不提示輸入高度
            這樣寫出來(lái)的文本高度是不變的
            包括使用該字型進(jìn)行的尺寸標(biāo)注。

            三問(wèn):如何改變已經(jīng)存在的字體格式?
            答:如果想改變已有文字的大小、字體、高寬比例、間距、傾斜角度、插入點(diǎn)等,
            最好利用“特性DDMODIFY)”命令(前提是你已經(jīng)定義好了許多文字格式)。
            點(diǎn)擊“特性”命令,點(diǎn)擊要修改的文字,回車,
            出現(xiàn)“修改文字”窗口,
            選擇要修改的項(xiàng)目進(jìn)行修改即可。

            四問(wèn):可以使用系統(tǒng)字體嗎,如宋體、楷體等?
            答:雖然ACAD R14能夠使用*.TTF漢字字體文件,
            可是這種字體太花費(fèi)CPU時(shí)間,
            對(duì)于檔次稍低的電腦,
            顯示速度(如使用實(shí)時(shí)縮放命令時(shí))實(shí)在太慢。
            建議檔次低的電腦還是使用*.shx文件好。
            在漢字*.shx文件當(dāng)中,
            推薦使用大恒或浩辰公司為ACADR12配套的HZ*.shx字體文件,
            但在不同機(jī)器上沒有相應(yīng)字體會(huì)引起漢字顯示問(wèn)題。
            當(dāng)然為美觀起見少量使用系統(tǒng)字庫(kù)也未嘗不可。

            五問(wèn) 何替換找不到的原文字體?
            復(fù)制要替換的字庫(kù)為將被替換的字庫(kù)名,如:打開一幅圖,提示未找到字體jd,你想用hztxt.shx替換它,那么你可以去找AutoCAD字體文件夾(font)把里面的hztxt.shx 復(fù)制一份,重新命名為jd.shx,然后在把XX.shx放到font里面,在重新打開此圖就可以了。以后如果你打開的圖包含jd這樣你機(jī)子里沒有的字體,就再也不會(huì)不停的要你找字體替換了。

            posted @ 2011-03-23 09:42 wrh 閱讀(3112) | 評(píng)論 (0)編輯 收藏

            使用字體的基本術(shù)語(yǔ),會(huì)讓設(shè)計(jì)過(guò)程中的交流變得非常容易。這里是一些基本的術(shù)語(yǔ),讓你的交流能夠更深入,而不是“嗯,那里,那個(gè),那個(gè)黑色的小玩意……”小寫e的"字懷"(“字谷”)有時(shí)候也被稱為是字“眼”,如果你還想更全面的了解其他術(shù)語(yǔ),可以去圖書館,或者是參考下面的網(wǎng)絡(luò)資源。(譯注:很遺憾,原文中鏈接的一些資源已經(jīng)無(wú)法訪問(wèn)了。但我們可以用google找到其他的資源。這些術(shù)語(yǔ)很重要,如果你希望閱讀西方的第一手的字體設(shè)計(jì)研究資料,這是必須跨越的一關(guān)。但目前在國(guó)內(nèi)的設(shè)計(jì)界似乎還沒有一個(gè)統(tǒng)一的翻譯,不少書籍的翻譯都是各行其是,讓讀者也無(wú)所適從。有時(shí)間我會(huì)找一篇比較完整的來(lái)翻譯。)

            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 閱讀(275) | 評(píng)論 (0)編輯 收藏

            所有的漢字或者英文都是下面的原理,
            由左至右,每8個(gè)點(diǎn)占用一個(gè)字節(jié),最后不足8個(gè)字節(jié)的占用一個(gè)字節(jié),而且從最高位向最低位排列。
            生成的字庫(kù)說(shuō)明:(以12×12例子)


            一個(gè)漢字占用字節(jié)數(shù):12÷8=1····4也就是占用了2×12=24個(gè)字節(jié)。
            編碼排序A0A0→A0FE A1A0→A2FE依次排列。
            以12×12字庫(kù)的“我”為例:“我”的編碼為CED2,所以在漢字排在CEH-AOH=2EH區(qū)的D2H-A0H=32H個(gè)。所以在12×12字庫(kù)的起始位置就是[{FE-A0}*2EH+32H]*24=104976開始的24個(gè)字節(jié)就是我的點(diǎn)陣模。
            其他的類推即可。
            英文點(diǎn)陣也是如此推理。

            在DOS程序中使用點(diǎn)陣字庫(kù)的方法

            回到頂部

                首先需要理解的是點(diǎn)陣字庫(kù)是一個(gè)數(shù)據(jù)文件,在這個(gè)數(shù)據(jù)文件里面保存了所有文字的點(diǎn)陣數(shù)據(jù).至于什么是點(diǎn)陣,我想我不講大家都知道 的,使用過(guò)"文曲星"之類的電子辭典吧,那個(gè)的液晶顯示器上面顯示的漢子就能夠明顯的看出"點(diǎn)陣"的痕跡.在 PC 機(jī)上也是如此,文字也是由點(diǎn)陣來(lái)組成了,不同的是,PC機(jī)顯示器的顯示分辨率更高,高到了我們?nèi)庋蹮o(wú)法區(qū)分的地步,因此"點(diǎn)陣"的痕跡也就不那么明顯了.

                點(diǎn)陣、矩陣、位圖這三個(gè)概念在本質(zhì)上是有聯(lián)系的,從某種程度上來(lái)講,這三個(gè)就是同義詞.點(diǎn)陣從本質(zhì)上講就是單色位圖,他使用一個(gè)比特來(lái)表示一個(gè)點(diǎn),如果這 個(gè)比特為0,表示某個(gè)位置沒有點(diǎn),如果為1表示某個(gè)位置有點(diǎn).矩陣和位圖有著密不可分的聯(lián)系,矩陣其實(shí)是位圖的數(shù)學(xué)抽象,是一個(gè)二維的陣列.位圖就是這種 二維的陣列,這個(gè)陣列中的 (x,y) 位置上的數(shù)據(jù)代表的就是對(duì)原始圖形進(jìn)行采樣量化后的顏色值.但是,另一方面,我們要面對(duì)的問(wèn)題是,計(jì)算機(jī)中數(shù)據(jù)的存放都是一維的,線性的.因此,我們需要 將二維的數(shù)據(jù)線性化到一維里面去.通常的做法就是將二維數(shù)據(jù)按行順序的存放,這樣就線性化到了一維.

                那么點(diǎn)陣字的數(shù)據(jù)存放細(xì)節(jié)到底是怎么樣的呢.其實(shí)也十分的簡(jiǎn)單,舉個(gè)例子最能說(shuō)明問(wèn)題.比如說(shuō) 16*16 的點(diǎn)陣,也就是說(shuō)每一行有16個(gè)點(diǎn),由于一個(gè)點(diǎn)使用一個(gè)比特來(lái)表示,如果這個(gè)比特的值為1,則表示這個(gè)位置有點(diǎn),如果這個(gè)比特的值為0,則表示這個(gè)位置沒 有點(diǎn),那么一行也就需要16個(gè)比特,而8個(gè)比特就是一個(gè)字節(jié),也就是說(shuō),這個(gè)點(diǎn)陣中,一行的數(shù)據(jù)需要兩個(gè)字節(jié)來(lái)存放.第一行的前八個(gè)點(diǎn)的數(shù)據(jù)存放在點(diǎn)陣數(shù) 據(jù)的第一個(gè)字節(jié)里面,第一行的后面八個(gè)點(diǎn)的數(shù)據(jù)存放在點(diǎn)陣數(shù)據(jù)的第二個(gè)字節(jié)里面,第二行的前八個(gè)點(diǎn)的數(shù)據(jù)存放在點(diǎn)陣數(shù)據(jù)的第三個(gè)字節(jié)里面,…,然后后 面的就以此類推了.這樣我們可以計(jì)算出存放一個(gè)點(diǎn)陣總共需要32個(gè)字節(jié).看看下面這個(gè)圖形化的例子:

                | |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| | |

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

                可以看出這是一個(gè)"漢"字的點(diǎn)陣,當(dāng)然文本的方式效果不是很好.根據(jù)上面的原則,我們可以寫出這個(gè)點(diǎn)陣的點(diǎn)陣數(shù) 據(jù):0x40,0x08,0x37,0xfc,0x10,0x08,…, 當(dāng)然寫這個(gè)確實(shí)很麻煩所以我不再繼續(xù)下去.我這樣做,也只是為了向你說(shuō)明,在點(diǎn)陣字庫(kù)中,每一個(gè)點(diǎn)陣的數(shù)據(jù)就是按照這種方式存放的.

                當(dāng)然也存在著不規(guī)則的點(diǎn)陣,這里說(shuō)的不規(guī)則,指的是點(diǎn)陣的寬度不是8的倍數(shù),比如 12*12 的點(diǎn)陣,那么這樣的點(diǎn)陣數(shù)據(jù)又是如何存放的呢?其實(shí)也很簡(jiǎn)單,每一行的前面8個(gè)點(diǎn)存放在一個(gè)字節(jié)里面,每一行的剩下的4點(diǎn)就使用一個(gè)字節(jié)來(lái)存放,也就是說(shuō) 剩下的4個(gè)點(diǎn)將占用一個(gè)字節(jié)的高4位,而這個(gè)字節(jié)的低4位沒有使用,全部都默認(rèn)的為零.這樣做當(dāng)然顯得有點(diǎn)浪費(fèi),不過(guò)卻能夠便于我們進(jìn)行存放和尋址.對(duì)于 其他不規(guī)則的點(diǎn)陣,也是按照這個(gè)原則進(jìn)行處理的.這樣我們可以得出一個(gè) m*n 的點(diǎn)陣所占用的字節(jié)數(shù)為 (m+7)/8*n.

                在明白了以上所講的以后,我們可以寫出一個(gè)顯示一個(gè)任意大小的點(diǎn)陣字模的函數(shù),這個(gè)函數(shù)的功能是輸出一個(gè)寬度為w,高度為h的字模到屏幕的 (x,y) 坐標(biāo)出,文字的顏色為 color,文字的點(diǎn)陣數(shù)據(jù)為 pdata 所指:

                /*輸出字模的函數(shù)*/

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

                {

                int     i;    /* 控制行 */

                int     j;    /* 控制一行中的8個(gè)點(diǎn) */

                int     k;    /* 一行中的第幾個(gè)"8個(gè)點(diǎn)"了 */

                int     nc;   /* 到點(diǎn)陣數(shù)據(jù)的第幾個(gè)字節(jié)了 */

                int     cols; /* 控制列 */

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

                w = (w + 7) / 8 * 8; /* 重新計(jì)算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++;

                }

                }

                }

                代碼很簡(jiǎn)單,不用怎么講解就能看懂,代碼可能不是最優(yōu)化的,但是應(yīng)該是最易讀懂的.其中的 putpixel 函數(shù),使用的是TC提供的 Graphics 中的畫點(diǎn)函數(shù).使用這個(gè)函數(shù)就可以完成點(diǎn)陣任意大小的點(diǎn)陣字模的輸出.

                接下來(lái)的問(wèn)題就是如何在漢子庫(kù)中尋址某個(gè)漢子的點(diǎn)陣數(shù)據(jù)了.要解決這個(gè)問(wèn)題,首先需要了解漢字在計(jì)算機(jī)中是如何表示的.在計(jì)算機(jī)中英文可以使用 ASCII 碼來(lái)表示,而漢字使用的是擴(kuò)展 ASCII 碼,并且使用兩個(gè)擴(kuò)展 ASCII 碼來(lái)表示一個(gè)漢字.一個(gè) ASCII 碼使用一個(gè)字節(jié)表示,所謂擴(kuò)展 ASCII 碼,也就是 ASCII 碼的最高位是1的 ASCII 碼,簡(jiǎn)單的說(shuō)就是碼值大于等于 128 的 ASCII 碼.一個(gè)漢字由兩個(gè)擴(kuò)展 ASCII 碼組成,第一個(gè)擴(kuò)展 ASCII 碼用來(lái)存放區(qū)碼,第二個(gè)擴(kuò)展 ASCII 碼用來(lái)存放位碼.在 GB2312-80 標(biāo)準(zhǔn)中,將所有的漢字分為94個(gè)區(qū),每個(gè)區(qū)有94個(gè)位可以存放94個(gè)漢字,形成了人們常說(shuō)的區(qū)位碼,這樣總共就有 94*94=8836 個(gè)漢字.在點(diǎn)陣字庫(kù)中,漢字點(diǎn)陣數(shù)據(jù)就是按照這個(gè)區(qū)位的順序來(lái)存放的,也就是最先存放的是第一個(gè)區(qū)的漢字點(diǎn)陣數(shù)據(jù),在每一個(gè)區(qū)中有是按照位的順序來(lái)存放 的.在漢字的內(nèi)碼中,漢字區(qū)位碼的存放實(shí)在擴(kuò)展 ASCII 基礎(chǔ)上存放的,并且將區(qū)碼和位碼都加上了32,然后存放在兩個(gè)擴(kuò)展 ASCII 碼中.具體的說(shuō)就是:

                第一個(gè)擴(kuò)展ASCII碼 = 128+32 + 漢字區(qū)碼

                第二個(gè)擴(kuò)展ASCII嗎 = 128+32 + 漢字位碼

                如果用char hz[2]來(lái)表示一個(gè)漢字,那么我可以計(jì)算出這個(gè)漢字的區(qū)位碼為:

                區(qū)碼 = hz[0] - 128 - 32 = hz[0] - 160

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

                這樣,我們可以根據(jù)區(qū)位碼在文件中進(jìn)行殉職了,尋址公式如下:

                漢字點(diǎn)陣數(shù)據(jù)在字庫(kù)文件中的偏移 = ((區(qū)碼-1) * 94 + 位碼) * 一個(gè)點(diǎn)陣字模占用的字節(jié)數(shù)

                在尋址以后,即可讀取漢字的點(diǎn)陣數(shù)據(jù)到緩沖區(qū)進(jìn)行顯示了.以下是實(shí)現(xiàn)代碼:

                /* 輸出一個(gè)漢字的函數(shù) */

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

                {

                char fONtbuf[128];   /* 足夠大的緩沖區(qū),也可以動(dòng)態(tài)分配 */

                int ch0 = (BYTE)hz[0]-0xA0; /* 區(qū)碼 */

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

                /* 計(jì)算偏移 */

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

                fseek(fp, offset, SEEK_SET);              /* 進(jìn)行尋址 */

                fread(fontbuf, 1, (w + 7) / 8 * h, fp);   /* 讀入點(diǎn)陣數(shù)據(jù) */

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

                }

                以上介紹完了中文點(diǎn)陣字庫(kù)的原理,當(dāng)然還有英文點(diǎn)陣字庫(kù)了.英文點(diǎn)陣字庫(kù)中單個(gè)點(diǎn)陣字模數(shù)據(jù)的存放方式與中文是一模一樣的,也就是對(duì)我們所寫的 _draw_model 函數(shù)同樣可以使用到英文字庫(kù)中.唯一不同的是對(duì)點(diǎn)陣字庫(kù)的尋址上.英文使用的就是 ASCII 碼,其碼值是0到127,尋址公式為:

                英文點(diǎn)陣數(shù)據(jù)在英文點(diǎn)陣字庫(kù)中的偏移 = 英文的ASCII碼 * 一個(gè)英文字模占用的字節(jié)數(shù)

                可以看到,區(qū)分中英文的關(guān)鍵就是,一個(gè)字符是 ASCII 碼還是擴(kuò)展 ASCII 碼,如果是 ASCII 碼,其范圍是0到127,這樣是使用的英文字庫(kù),如果是擴(kuò)展 ASCII 碼,則與其后的另一個(gè)擴(kuò)展 ASCII 碼組成漢字內(nèi)碼,使用中文字庫(kù)進(jìn)行顯示.只要正確區(qū)分 ASCII 碼的類型并進(jìn)行分別的處理,也就能實(shí)現(xiàn)中英文字符串的混合輸出了.

            點(diǎn)陣字庫(kù)和矢量字庫(kù)的差別

            回到頂部

                我們都只知道,各種字符在電腦屏幕上都是以一些點(diǎn)來(lái)表示的,因此也叫點(diǎn)陣.最早的字庫(kù)就是直接把這些點(diǎn)存儲(chǔ)起來(lái),就是點(diǎn)陣字庫(kù).常見的漢字點(diǎn)陣字庫(kù)有 16x16, 24x24 等.點(diǎn)陣字庫(kù)也有很多種,主要區(qū)別在于其中存儲(chǔ)編碼的方式不同.點(diǎn)陣字庫(kù)的最大缺點(diǎn)就是它是固定分辨率的,也就是每種字庫(kù)都有固定的大小尺寸,在原始尺寸下使用,效果很好,但如果將其放大或縮小使用,效果就很糟糕了,就會(huì)出現(xiàn)我們通常說(shuō)的鋸齒現(xiàn)象.因?yàn)樾枰淖煮w大小組合有無(wú)數(shù)種,我們也不可能為每種大小都定義一個(gè)點(diǎn)陣字庫(kù).于是就出現(xiàn)了矢量字庫(kù).

                矢量字庫(kù)

                矢量字庫(kù)是把每個(gè)字符的筆劃分解成各種直線和曲線,然后記下這些直線和曲線的參數(shù),在顯示的時(shí)候,再根據(jù)具體的尺寸大小,畫出這些線條,就還原了原來(lái)的字符.它的好處就是可以隨意放大縮小而不失真.而且所需存儲(chǔ)量和字符大小無(wú)關(guān).矢量字庫(kù)有很多種,區(qū)別在于他們采用的不同數(shù)學(xué)模型來(lái)描述組成字符的線條.常見的矢量字庫(kù)有 Type1字庫(kù)和Truetype字庫(kù).

                在點(diǎn)陣字庫(kù)中,每個(gè)字符由一個(gè)位圖表示(如圖2.5所示),并把它用一個(gè)稱為字符掩膜的矩陣來(lái)表示,其中的每個(gè)元素都是一位二進(jìn)制數(shù),如果該位為1表示字符的筆畫經(jīng)過(guò)此位,該像素置為字符顏色;如果該位為0,表示字符的筆畫不經(jīng)過(guò)此位,該像素置為背景顏色.點(diǎn)陣字符的顯示分為兩步:首先從字庫(kù)中將它的位圖檢索出來(lái),然后將檢索到的位圖寫到幀緩沖器中.

                在實(shí)際應(yīng)用中,同一個(gè)字符有多種字體(如宋體、楷體等),每種字體又有多種大小型號(hào),因此字庫(kù)的存儲(chǔ)空間十分龐大.為了減少存儲(chǔ)空間,一般采用壓縮技術(shù).

                矢量字符記錄字符的筆畫信息而不是整個(gè)位圖,具有存儲(chǔ)空間小,美觀、變換方便等優(yōu)點(diǎn).例如:在AutoCAD中使用圖形實(shí)體-形(Shape)-來(lái)定義矢量字符,其中,采用了直線和圓弧作為基本的筆畫來(lái)對(duì)矢量字符進(jìn)行描述. 對(duì)于字符的旋轉(zhuǎn)、放大、縮小等幾何變換,點(diǎn)陣字符需要對(duì)其位圖中的每個(gè)象素進(jìn)行變換,而矢量字符則只需要對(duì)其幾何圖素進(jìn)行變換就可以了,例如:對(duì)直線筆畫的兩個(gè)端點(diǎn)進(jìn)行變換,對(duì)圓弧的起點(diǎn)、終點(diǎn)、半徑和圓心進(jìn)行變換等等.

                矢量字符的顯示也分為兩步.首先從字庫(kù)中將它的字符信息.然后取出端點(diǎn)坐標(biāo),對(duì)其進(jìn)行適當(dāng)?shù)膸缀巫儞Q,再根據(jù)各端點(diǎn)的標(biāo)志顯示出字符.

                輪廓字形法是當(dāng)今國(guó)際上最流行的一種字符表示方法,其壓縮比大,且能保證字符質(zhì)量.輪廓字形法采用直線、B樣條/Bezier曲線的集合來(lái)描述一個(gè)字符的輪廓線.輪廓線構(gòu)成一個(gè)或若干個(gè)封閉的平面區(qū)域.輪廓線定義加上一些指示橫寬、豎寬、基點(diǎn)、基線等等控制信息就構(gòu)成了字符的壓縮數(shù)據(jù).

            如何使用Windows的系統(tǒng)字庫(kù)生成點(diǎn)陣字庫(kù)?

            回到頂部

                我的程序現(xiàn)在只能預(yù)覽一個(gè)漢字的不同字體的點(diǎn)陣表達(dá).

                界面很簡(jiǎn)單:   一個(gè)輸出點(diǎn)陣大小的選擇列表(8x8,16x16,24x24等),一個(gè)系統(tǒng)中已有的字體名稱列表,一個(gè)預(yù)覽按鈕,一塊畫圖顯示區(qū)域.

                得到字體列表的方法:(作者稱這一段是用來(lái)取回系統(tǒng)的字體,然后添加到下拉框中)

                //取字體名稱列表的回調(diào)函數(shù),使用前要聲明一下該方法

                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;

                }

                //說(shuō)明:CFontPeekerDlg   是我的dialog的類名,   m_combo_sfont是列表名稱下拉combobox關(guān)聯(lián)的control變量

                //調(diào)用的地方     (******問(wèn)題1:下面那個(gè)&lf怎么得到呢……)

                {

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

                m_combo_sfont.SetCurSel(0);

                }

                字體預(yù)覽:

                如果點(diǎn)陣大小選擇16,顯示的時(shí)候就畫出16x16個(gè)方格.自定義一個(gè)類CMyStatic繼承自CStatic,用來(lái)畫圖.在CMyStatic的OnPaint()函數(shù)中計(jì)算并顯示.

                取得字體:

                常用的方法:用CreateFont創(chuàng)建字體,把字TextOut再用GetPixel()取點(diǎn)存入數(shù)組.   缺點(diǎn):必須把字TextOut出來(lái),能在屏幕上看見,不爽.

                我的方法,用這個(gè)函數(shù):GetGlyphOutline(),可以得到一個(gè)字的輪廓矢量或者位圖.可以不用textout到屏幕,直接取得字模信息

                函數(shù)原型如下:

                DWORD   GetGlyphOutline(

                HDC   hdc,                     //畫圖設(shè)備句柄

                UINT   uChar,                 //將要讀取的字符/漢字

                UINT   uFormat,             //返回?cái)?shù)據(jù)的格式(字的外形輪廓還是字的位圖)

                LPGLYPHMETRICS   lpgm,     //   GLYPHMETRICS結(jié)構(gòu)地址,輸出參數(shù)

                DWORD   cbBuffer,       //輸出數(shù)據(jù)緩沖區(qū)的大小

                LPVOID   lpvBuffer,     //輸出數(shù)據(jù)緩沖區(qū)的地址

                CONST   MAT2   *lpmat2   //轉(zhuǎn)置矩陣的地址

                );

                說(shuō)明:

                uChar字符需要判斷是否是漢字還是英文字符.中文占2個(gè)字節(jié)長(zhǎng)度.

                lpgm是輸出函數(shù),調(diào)用GetGlyphOutline()是無(wú)須給lpgm   賦值.

                lpmat2如果不需要轉(zhuǎn)置,將   eM11.value=1;   eM22.value=1;   即可.

                cbBuffer緩沖區(qū)的大小,可以先通過(guò)調(diào)用GetGlyphOutline(……lpgm,   0,   NULL,   mat);   來(lái)取得,然后動(dòng)態(tài)分配lpvBuffer,再一次調(diào)用GetGlyphOutline,將信息存到lpvBuffer.   使用完畢后再釋放lpvBuffer.

                程序示例:(***問(wèn)題2:用這段程序,我獲取的字符點(diǎn)陣總都是一樣的,不管什么字……)

                ……前面部分省略……

                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字節(jié)(DWORD)對(duì)齊

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

                }

                else

                {

                if(m_pFont!=NULL)

                delete   m_pFont;

                return;

                }

                編程中遇到問(wèn)題:

                一開始,GetGlyphOutline總是返回-1,getLastError顯示是"無(wú)法完成的功能",后來(lái)發(fā)現(xiàn)是因?yàn)檎{(diào)用之前沒有給hdc設(shè)置Font.

                后來(lái)能取得pBuf信息后,又開始郁悶,因?yàn)椴惶靼譩itmap的結(jié)果是按什么排列的.后來(lái)跟蹤漢字"一"來(lái)調(diào)試(這個(gè)字簡(jiǎn)單),注意到了glyph.gmBlackBoxX   其實(shí)就是輸出位圖的寬度,glyph.gmBlackBoxY就是高度.如果gmBlackBoxX=15,glyph.gmBlackBoxY=2,表示輸出的pBuf中有這些信息:位圖有2行信息,每一行使用15   bit來(lái)存儲(chǔ)信息.

                例如:我讀取"一":glyph.gmBlackBoxX   =   0x0e,glyph.gmBlackBoxY=0x2;     pBuf長(zhǎng)度cbBuf=8   字節(jié)

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

                字符寬度   0x0e=14     則   第一行信息為:           0000   0000   0000   100       (只取到前14位)

                第二行根據(jù)4字節(jié)對(duì)齊的規(guī)則,從0xff開始         1111   1111   1111   110

                看出"一"字了嗎?呵呵

                直到他的存儲(chǔ)之后就可以動(dòng)手解析輸出的信息了.

                我定義了一個(gè)宏#define   BIT(n)     (1<<(n))     用來(lái)比較每一個(gè)位信息時(shí)使用

                后來(lái)又遇到了一個(gè)問(wèn)題,就是小頭和大頭的問(wèn)題了.在我的機(jī)器上是little   endian的形式,如果我用

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

                //j   from   0   to   15

                if(   *lptr   &   BIT(j)   )

                {

                //這時(shí)候如果想用j來(lái)表示寫1的位數(shù),就錯(cuò)了

                }

                因?yàn)閺淖止?jié)數(shù)組中轉(zhuǎn)化成unsigned   long型的時(shí)候,數(shù)值已經(jīng)經(jīng)過(guò)轉(zhuǎn)化了,像上例中,實(shí)際上是0x0800   在同BIT(j)比較.

                不多說(shuō)了,比較之前轉(zhuǎn)化一下就可以了if(   htonl(*lptr)   &   BIT(j)   )

            Unicode中文點(diǎn)陣字庫(kù)的生成與使用

            回到頂部

                點(diǎn)陣字庫(kù)包含兩部分信息.首先是點(diǎn)陣字庫(kù)文件頭信息,它包含點(diǎn)陣字庫(kù)文字的字號(hào)、多少位表示一個(gè)像素,英文字母與符號(hào)的size、起始和結(jié)束unicode編碼、在文件中的起始偏移,漢字的size、起始和結(jié)束unicode編碼、在文件中的起始偏移.然后是真實(shí)的點(diǎn)陣數(shù)據(jù),即一段段二進(jìn)制串,每一串表示一個(gè)字母、符號(hào)或漢字的點(diǎn)陣信息.

                要生成點(diǎn)陣字庫(kù)必須有文字圖形的來(lái)源,我的方法是使用ttf字體.ttf字體的顯示采用的是SDL_ttf庫(kù),這是開源圖形庫(kù)SDL的一個(gè)擴(kuò)展庫(kù),它使用的是libfreetype以讀取和繪制ttf字體.

                它提供了一個(gè)函數(shù),通過(guò)傳入一個(gè)Unicode編碼便能輸出相應(yīng)的文字的帶有alpha通道的位圖.那么我們可以掃描這個(gè)位圖以得到相應(yīng)文字的點(diǎn)陣信息.由于帶有alpha通道,我們可以在點(diǎn)陣信息中也加入權(quán)值,使得點(diǎn)陣字庫(kù)也有反走樣效果.我采用兩位來(lái)表示一個(gè)點(diǎn),這樣會(huì)有三級(jí)灰度(還有一個(gè)表示透明).

                點(diǎn)陣字庫(kù)的顯示首先需要將文件頭信息讀取出來(lái),然后根據(jù)unicode編碼判斷在哪個(gè)區(qū)間內(nèi),然后用unicode編碼減去此區(qū)間的起始unicode編碼,算出相對(duì)偏移,并加上此區(qū)間的文件起始偏移得到文件的絕對(duì)偏移,然后讀出相應(yīng)位數(shù)的數(shù)據(jù),最后通過(guò)掃描這段二進(jìn)制串,在屏幕的相應(yīng)位置輸出點(diǎn)陣字型.

                顯示點(diǎn)陣字體需要頻繁讀取文件,因此最好做一個(gè)固定大小的緩存,采用LRU置換算法維護(hù)此緩存,以減少磁盤讀取.

            標(biāo)準(zhǔn)點(diǎn)陣字庫(kù)芯片

            回到頂部

            標(biāo)準(zhǔn)點(diǎn)陣字庫(kù)芯片的特點(diǎn):

                1.內(nèi)涵全國(guó)信標(biāo)委授權(quán)的標(biāo)準(zhǔn)點(diǎn)陣字型數(shù)據(jù)、

                2.支持國(guó)標(biāo)字符集GB2312(6,763漢字),GB18030(27,484漢字).

                3.支持多種點(diǎn)陣字型,包括11×12點(diǎn),15×16點(diǎn),24×24點(diǎn),32×32點(diǎn).

                4.免除了字庫(kù)燒錄和測(cè)試工序,并節(jié)省了2%以上的燒錄損耗.

                5.價(jià)格相當(dāng)于空白FLASH價(jià)格

            標(biāo)準(zhǔn)點(diǎn)陣字庫(kù)芯片的種類和應(yīng)用

            回到頂部

            51單片機(jī)的13×14點(diǎn)陣縮碼漢卡

            回到頂部

                我們歷時(shí)數(shù)載,開發(fā)成"51單片機(jī)13×14點(diǎn)陣縮碼漢卡",適用于目前國(guó)內(nèi)外應(yīng)用最為廣泛的MCSX-51及其兼容系列單片機(jī).

                與此同時(shí),還開發(fā)了13×14點(diǎn)陣漢字字模.13×14點(diǎn)陣字模,可完全與目前通用的16×16點(diǎn)陣漢字字模媲美,其在單片機(jī)和嵌入式系統(tǒng)的漢字顯示應(yīng)用中也具有明顯的經(jīng)濟(jì)價(jià)值和實(shí)用意義.

                1.單片機(jī)目前的漢字顯示

                信息交流的最主要方式之一即文字交流,但由于我國(guó)方塊漢字?jǐn)?shù)量繁多,構(gòu)形迥異,使?jié)h字顯示一直是我國(guó)計(jì)算機(jī)普及的障礙.隨著計(jì)算機(jī)技術(shù)的迅速發(fā)展,PC機(jī)的漢字顯示已不成問(wèn)題.但對(duì)于成本低、體積小、應(yīng)用靈活且用量極為巨大的單片機(jī)而言,因其結(jié)構(gòu)簡(jiǎn)單,硬件資源十分有限,其漢字顯示仍面對(duì)著捉襟見肘,力不從心的窘境.

                目前單片機(jī)的漢字顯示有三種基本方法.

                ①采用標(biāo)準(zhǔn)字庫(kù)法.即將國(guó)標(biāo)漢字庫(kù)固人ROM中,將單片機(jī)的硬件和軟件進(jìn)行特別擴(kuò)展后以顯示漢字.眾所周知,即使是16×16點(diǎn)陣標(biāo)準(zhǔn)字庫(kù),也須占用200KB以上的單元內(nèi)存,而就目前主流5l系列單片機(jī)而言,最大尋址范圍僅64KB,即使程序區(qū)與數(shù)據(jù)區(qū)合起來(lái)也僅128KB內(nèi)存.因此,若不加特別的擴(kuò)展設(shè)計(jì),不要說(shuō)檢字程序和用戶空間,僅字庫(kù)都裝不下.這種方法雖然可以方便地使用現(xiàn)成標(biāo)準(zhǔn)字庫(kù),但卻需占用大量的硬件和軟件資源,增加很大一部分成本和設(shè)計(jì)難度,所以不經(jīng)常使用.

                ②字模直接固化法.即將所顯示的漢字,依先后順序?qū)⑵渥帜R灰粡臉?biāo)準(zhǔn)字庫(kù)中提取后,重新固化,予以顯示.此法雖為簡(jiǎn)捷,但只適于顯示少量漢字,且字模的制取繁瑣,軟件的修改維護(hù)都很困難.

                ③帶索引小字庫(kù)法.即將欲顯示文件中的漢字字模,從標(biāo)準(zhǔn)字庫(kù)中逐一提取固化,制成小型字庫(kù),并按其在小字庫(kù)中的位置制成索引表,顯示時(shí)從索引表查出其新的字模取碼地址,取碼顯示.此方法雖比較靈活,可顯示較多的漢字,但仍然局限于只能顯示固定文件內(nèi)容,且字模制取同樣麻煩.

                一種較新的單片機(jī)"漢字動(dòng)態(tài)編碼與顯示方案"(見《單片機(jī)與嵌入式系統(tǒng)應(yīng)用》雜志

                由上可見,目前單片機(jī)各種漢字顯示方案均不理想.標(biāo)準(zhǔn)字庫(kù)法,單片機(jī)不堪重負(fù);而其它方法最大且又無(wú)法克服的缺點(diǎn)是,所顯示文字皆有局限.顯示內(nèi)容也皆須專業(yè)人員設(shè)計(jì)而定,用戶難于更改.這便極大地限制了單片機(jī)在各個(gè)領(lǐng)域的開拓和應(yīng)用.究其原因,皆為單片機(jī)本身無(wú)漢卡,而這也正是我們致力于"51漢卡"開發(fā)的初衷.

                2.13×14點(diǎn)陣漢字字模

                為墊定"5l漢卡"的字型基礎(chǔ),首先開發(fā)成了l3×14點(diǎn)陣漢字字模.在目前通用的漢字字模中,最簡(jiǎn)單的是16×16點(diǎn)陣字模.在微型打字機(jī)中,也偶見有12×12點(diǎn)陣字模,但實(shí)用中不多見.字模點(diǎn)陣數(shù)直接決定著每一漢字所占單元內(nèi)存值,能否在保證字模準(zhǔn)確、美觀的基礎(chǔ)上,尋找一種較少的點(diǎn)陣字模呢?這便是我們最初的想法.于是我們經(jīng)過(guò)反復(fù)選擇比較,終于在國(guó)內(nèi)首個(gè)推出了13×14點(diǎn)陣字模.此設(shè)計(jì),一是基于我國(guó)漢字為方塊字,故其行、列值需相近;二是漢字多有對(duì)稱1生,故其列值宜奇不宜偶.設(shè)計(jì)實(shí)際表明,若行、列值很少,則難保證字模的準(zhǔn)確性和美觀性.?

                13×14點(diǎn)陣字模,是以我國(guó)現(xiàn)行簡(jiǎn)化字為準(zhǔn),并在此基礎(chǔ)上設(shè)計(jì)而成.與目前通用的漢字16×l6點(diǎn)陣字模相比,其準(zhǔn)確性和美觀性并不遜色.然而其單字所占內(nèi)存卻由32個(gè)單元降至26個(gè)單元;另外使得每個(gè)單字顯示由原來(lái)的256個(gè)像素降至l82個(gè)像素,使顯示成本和空間均減少近三分之一.100×200點(diǎn)陣LED字屏,可顯示16×l6點(diǎn)陣漢字72個(gè),而l3×14點(diǎn)陣漢字便可顯示l05個(gè),且顯示效果并無(wú)太大差異.這無(wú)疑對(duì)單片機(jī)和嵌入式系統(tǒng)漢字顯示產(chǎn)品的開發(fā)和應(yīng)用,具有明顯的經(jīng)濟(jì)價(jià)值和實(shí)用意義.

                3.51單片機(jī)13×14點(diǎn)陣縮碼漢卡

                "51漢卡"依據(jù)我國(guó)的漢字特點(diǎn)和單片機(jī)的快速構(gòu)字功能,在13×14點(diǎn)陣字模基礎(chǔ)上,以縮碼形式開發(fā)而成單片機(jī)漢卡的開發(fā),應(yīng)以目前通用的主流單片機(jī)為研發(fā)對(duì)象,還應(yīng)在囊括國(guó)標(biāo)一、二級(jí)漢字及常用字符的前提下,使內(nèi)存占用必須降至主流單片機(jī)可尋址范圍內(nèi),且需留有足夠的檢字程序和用戶應(yīng)用空間.另外,字模設(shè)計(jì)必須準(zhǔn)確、美觀.字模提取速度也必須滿足實(shí)用要求."51漢卡"的開發(fā)正是依據(jù)原則,并達(dá)到了以上各項(xiàng)要求.

                顧名思義,"51漢卡,即以MCS-51系列及其兼容單片機(jī)為研發(fā)對(duì)象.以51系列為代表的8位單片機(jī),在過(guò)去、現(xiàn)在以及可以予見的將來(lái),都將是嵌入式系統(tǒng)低端應(yīng)用的主流機(jī)型.此乃業(yè)界專家的共識(shí).

                "51漢卡"囊括了"GB2312-80"國(guó)標(biāo)字庫(kù)的全部一、二級(jí)漢字,并增補(bǔ)漢字86個(gè);同時(shí)包括了大、小英文字母、阿拉伯?dāng)?shù)字等160個(gè)常用字符和不到4KB的構(gòu)字程序,卻僅總共占用了不足66KB的內(nèi)存.每字平均約占9.8個(gè)單元,相對(duì)于16×16點(diǎn)陣每字占32單兀內(nèi)存而言,尚不到其三分之一.這對(duì)于具有相互獨(dú)立的64KB程序區(qū)和64KB數(shù)據(jù)區(qū)的51系列單片機(jī)而言,若適當(dāng)配置內(nèi)存,可為檢字程序和用戶留出90%以上的程序空間及相當(dāng)數(shù)量的數(shù)據(jù)空間,對(duì)于一般用戶的應(yīng)用,都將綽綽有余.

                另外,為使"51漢卡''更便于使用和進(jìn)一步節(jié)省內(nèi)存,在上述基礎(chǔ)上又開發(fā)成一套簡(jiǎn)化版本,刪去了部分較偏僻的二級(jí)漢字.簡(jiǎn)化版本包括約5580個(gè)漢字,共占用內(nèi)存58KB.實(shí)際上,按有關(guān)權(quán)威部門的統(tǒng)計(jì),一般文本99%的文字是由2400個(gè)字寫成的,因此使用簡(jiǎn)化版本,并配以簡(jiǎn)單的造字程序,一般亦可滿足我們的使用要求.

                "51漢卡"所用字模,即我們開發(fā)的完全可與16×16點(diǎn)陣字模媲美的I3×14點(diǎn)陣漢字字模.字模提取速度是我們最為關(guān)心的問(wèn)題之一.經(jīng)測(cè)試及實(shí)際使用表明,"51漢卡''的提模速度完全可滿足單片機(jī)漢字顯示的實(shí)用要求.

                我們使用INTEL公司MCS-51經(jīng)典系列87C51單片機(jī)在24MHz頻率下測(cè)試,平均字模提取速度為2.1ms/字.因人的視覺暫留時(shí)間為0.1s,無(wú)論理論還是實(shí)際使用都表明,50字字模提取并顯示,并無(wú)遲滯和待機(jī)之感.即使在1?2MHz頻率下,20字取模,即點(diǎn)即出,在一般拼音檢字和少量漢字顯示中,完全可滿足使用要求.隨著單片機(jī)技術(shù)的迅速發(fā)展,目前,INTEL公司、Atmel公司、philips公司、我國(guó)臺(tái)灣華邦等公司生產(chǎn)的MCS-51兼容單片機(jī)時(shí)鐘頻率可達(dá)33MHz,增強(qiáng)型可達(dá)40MHz,以至達(dá)60MHz;現(xiàn)市售的"STC89LE"系列單片機(jī),最高頻率可達(dá)90MHz.這些芯片都完全能與MCS-51芯片兼容,對(duì)于更高需求的場(chǎng)合,更新升級(jí)也十分簡(jiǎn)便.另外,在單片機(jī)和嵌入式系統(tǒng)中,文字顯示速度要求并不高,只要滿足換屏?xí)r的視覺要求即可.其漢字顯示字?jǐn)?shù),一般也不太多.如用LCD顯示屏,128×64點(diǎn)陣,才顯示32個(gè)字;192×64點(diǎn)陣才顯48個(gè)字;即使使用l3×14點(diǎn)陣字模,滿屏也才56個(gè)漢字.

                4."51漢卡"設(shè)計(jì)依據(jù)及說(shuō)明

                "51漢卡"設(shè)計(jì)依據(jù)是,我國(guó)漢字雖然數(shù)量繁多,字型各異,但其中復(fù)合結(jié)構(gòu)者占大部分,并素有"偏旁取義,正字取音"之說(shuō).如"寸"字與不同偏旁可組成"村"、"付"、"討"、"守"、"過(guò)"等字.因此"51漢卡"除單結(jié)構(gòu)字基本以全碼設(shè)計(jì)外,復(fù)臺(tái)結(jié)構(gòu)字多用相應(yīng)的單體字及其偏旁,以結(jié)構(gòu)代碼寫成.利用單片機(jī)快速的單元積木式構(gòu)字程序,便可迅速生成字模代碼.這既保證了提碼速度,又節(jié)省了大量的漢卡內(nèi)存.

                有關(guān)"51漢卡"的幾點(diǎn)說(shuō)明如下:

                ①凡漢字庫(kù)中簡(jiǎn)、繁體字都有的用簡(jiǎn)體.如"後"以"后"代,"馀"以"余"代等;

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

                ③對(duì)于多體字,一般以常用字代,如"摺"以"折"代,"鏇"以"旋,代等,但"吒"不以"咤"代,"讎"不以"仇"代等;

                ④對(duì)通常已由其它字取代的字,都以這些字代替,如"崠"以"東"代,"肛''以"船"代等;

                ⑤二級(jí)漢字中,不單獨(dú)構(gòu)成漢字的偏旁未收入;

                ⑥依據(jù)名篇名著,生活用語(yǔ)等,增補(bǔ)漢字86個(gè);

                ⑦收編大、小寫英文字母、阿拉伯?dāng)?shù)字、標(biāo)點(diǎn)符號(hào)等各種常用字符160個(gè).

                5."51單片機(jī)漢卡"應(yīng)用舉例

                利用"51單片機(jī)漢卡",將使51系列單片機(jī)的漢字顯示輕而易舉,并可大為降低成本、體積和設(shè)計(jì)開發(fā)的難度,為單片機(jī)在生產(chǎn)控制、信息通信、文化教育和日常生活等領(lǐng)域,特別是計(jì)算機(jī)終端和手持產(chǎn)品的開發(fā)提供極大的便利和支持.?

                我們現(xiàn)已初步開發(fā)成"51漢卡"的"區(qū)位碼輸入法"和"拼音輸入法,檢字程序,并利用"51漢卡"成功地開發(fā)了帶有廉價(jià)單片機(jī)控制器的LED漢字顯示屏.這不僅大幅度降低了成本費(fèi)用.而且用戶可以通過(guò)單片機(jī)控制器,隨心所欲地改變顯示內(nèi)容.

                51硬件設(shè)計(jì)

                CPU--87C51、12MHz晶振.

             

                程序存儲(chǔ)器一1片EPROM?27C512.

                數(shù)據(jù)存儲(chǔ)器一1片EPROM?27C512;1片EEPROM28C64A;1片6116.

                控制器顯示屏一LCD?HY一19264B(深圳秋田視佳實(shí)業(yè)有限公司).

                LED屏選240×16點(diǎn)陣.

                本系統(tǒng)用標(biāo)準(zhǔn)小鍵盤檢字,一次可予選4000字;控制器LCD滿屏顯示l3×14點(diǎn)陣漢字56個(gè);LED屏滿屏顯示漢字19個(gè).

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

                5.2程序設(shè)計(jì)框圖

                程序設(shè)計(jì)流程如圖1所示.本系統(tǒng)采用12MHz晶振,若LCD取滿屏56字,換屏?xí)r有約0.1s的延時(shí),這對(duì)人的實(shí)際視覺并無(wú)大影響.

            標(biāo)準(zhǔn)點(diǎn)陣漢字字庫(kù)芯片

            回到頂部

                1 概述

                GT23L24M1W是一款內(nèi)含24X24點(diǎn)陣的漢字庫(kù)芯片,支持GB18030國(guó)標(biāo)漢字(含有國(guó)家信標(biāo)委合法授

                權(quán))及ASCII字符.排列格式為橫置橫排.用戶通過(guò)字符內(nèi)碼,利用本手冊(cè)提供的方法計(jì)算出該字符點(diǎn)陣

                在芯片中的地址,可從該地址連續(xù)讀出字符點(diǎn)陣信息.

                1.1 芯片特點(diǎn)

                ●  數(shù)據(jù)總線: SPI 串行總線接口

                PLII 精簡(jiǎn)地址并行總線接口

                ●  點(diǎn)陣排列方式:字節(jié)橫置橫排

                訪問(wèn)速度:SPI 時(shí)鐘頻率:20MHz(max.)

                PLII 訪問(wèn)速度:130ns(max.) @3.3V

                ●  工作電壓:2.7V~3.6V

                ●  電流:工作電流:12mA

                待機(jī)電流:10uA

                ●  封裝:SO20W

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

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

                1.2 字庫(kù)內(nèi)容

                字型樣張

                2 引腳描述與接口連接

                2.1 引腳名稱

                2.2 SPI 接口引腳描述

                串行數(shù)據(jù)輸出(SO):該信號(hào)用來(lái)把數(shù)據(jù)從芯片串行輸出,數(shù)據(jù)在時(shí)鐘的下降沿移出.

                串行數(shù)據(jù)輸入(SI):該信號(hào)用來(lái)把數(shù)據(jù)從串行輸入芯片,數(shù)據(jù)在時(shí)鐘的上升沿移入.

                串行時(shí)鐘輸入(SCLK):數(shù)據(jù)在時(shí)鐘上升沿移入,在下降沿移出.

                片選輸入(CS#):所有串行數(shù)據(jù)傳輸開始于CE#下降沿,CE#在傳輸期間必須保持為低電平,在兩條

                指令之間保持為高電平.

                總線掛起輸入(HOLD#):

                2.3 SPI 接口與主機(jī)接口電路示意圖

                SPI 與主機(jī)接口電路連接可以參考下圖(#HOLD管腳建議接 2K 電阻 3.3V 拉高).

                若是采用系統(tǒng)電壓為 5V的,則需要進(jìn)行電平轉(zhuǎn)換匹配連接 GT23 芯片,可以參考下圖(#HOLD 管腳建議接 2K 電阻 3.3V 拉高).

                2.4 PLII 接口引腳描述

                2.5 PLII 接口與主機(jī)接口電路示意圖

                SPI/PLII_SEL(管腳內(nèi)部有 100K 上拉電阻)接地,字庫(kù)芯片選擇 PLII 接口模式,與主機(jī)接口電路連接可以參考下圖.

                2.6 PLII 總線接口尋址說(shuō)明

                在 PLII 總線模式下,芯片內(nèi)部有 3個(gè)地址寄存器,主機(jī)需要把要讀取數(shù)據(jù)的地址寫入這 3個(gè)地址寄存器,然后再?gòu)臄?shù)據(jù)寄存器中讀出數(shù)據(jù).主機(jī)每讀一次數(shù)據(jù)寄存器,芯片內(nèi)部的地址寄存器會(huì)自動(dòng)增一,從而使主機(jī)只寫一次首地址,就可以連續(xù)讀取數(shù)據(jù).

                3 字庫(kù)調(diào)用方法

                3.1 漢字點(diǎn)陣排列格式

                每個(gè)漢字在芯片中是以漢字點(diǎn)陣字模的形式存儲(chǔ)的,每個(gè)點(diǎn)用一個(gè)二進(jìn)制位表示,存 1的點(diǎn),當(dāng)顯示

                時(shí)可以在屏幕上顯示亮點(diǎn),存 0的點(diǎn),則在屏幕上不顯示.點(diǎn)陣排列格式為橫置橫排:即一個(gè)字節(jié)的高位

                表示左面的點(diǎn),低位表示右面的點(diǎn)(如果用戶按 word mode讀取點(diǎn)陣數(shù)據(jù),請(qǐng)注意高低字節(jié)的順序),排

                滿一行的點(diǎn)后再排下一行.這樣把點(diǎn)陣信息用來(lái)直接在顯示器上按上述規(guī)則顯示,則將出現(xiàn)對(duì)應(yīng)的漢字.

                3.1.1  24X24點(diǎn)漢字排列格式

                24X24 點(diǎn)漢字的信息需要 72個(gè)字節(jié)(BYTE 0 – BYTE 71)來(lái)表示.該 24X24 點(diǎn)漢字的點(diǎn)陣數(shù)據(jù)是

                橫置橫排的,其具體排列結(jié)構(gòu)如下圖:

                命名規(guī)則:

                最大字符集及字?jǐn)?shù)

                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 閱讀(5108) | 評(píng)論 (0)編輯 收藏

            在c語(yǔ)言里可以用system()系統(tǒng)調(diào)用來(lái)處理

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


                  


            深入淺出ShellExecute   
            譯者:徐景周(原作:Nishant   S) 

            Q:   如何打開一個(gè)應(yīng)用程序?   ShellExecute(this-> m_hWnd, "open ", "calc.exe ", " ", " ",   SW_SHOW   ); 
            或   ShellExecute(this-> m_hWnd, "open ", "notepad.exe ", 
                    "c:\\MyLog.log ", " ",SW_SHOW   ); 
            正如您所看到的,我并沒有傳遞程序的完整路徑。 
            Q:   如何打開一個(gè)同系統(tǒng)程序相關(guān)連的文檔?   ShellExecute(this-> m_hWnd, "open ", 
                    "c:\\abc.txt ", " ", " ",SW_SHOW   ); 
            Q:   如何打開一個(gè)網(wǎng)頁(yè)?   ShellExecute(this-> m_hWnd, "open ", 
                    "http://www.google.com ", " ", " ",   SW_SHOW   ); 
            Q:   如何激活相關(guān)程序,發(fā)送EMAIL?   ShellExecute(this-> m_hWnd, "open ", 
                    "mailto:nishinapp@yahoo.com ", " ", " ",   SW_SHOW   ); 
            Q:   如何用系統(tǒng)打印機(jī)打印文檔?   ShellExecute(this-> m_hWnd, "print ", 
                    "c:\\abc.txt ", " ", " ",   SW_HIDE); 
            Q:   如何用系統(tǒng)查找功能來(lái)查找指定文件?   ShellExecute(m_hWnd, "find ", "d:\\nish ", 
                    NULL,NULL,SW_SHOW); 
            Q:   如何啟動(dòng)一個(gè)程序,直到它運(yùn)行結(jié)束?   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 閱讀(3461) | 評(píng)論 (3)編輯 收藏

            在圖形圖象處理編程過(guò)程中,雙緩沖是一種基本的技術(shù)。我們知道,如果窗體在響應(yīng)WM_PAINT消息的時(shí)候要進(jìn)行復(fù)雜的圖形處理,那么窗體在重繪時(shí)由于過(guò)頻的刷新而引起閃爍現(xiàn)象。解決這一問(wèn)題的有效方法就是雙緩沖技術(shù)。

                因?yàn)榇绑w在刷新時(shí),總要有一個(gè)擦除原來(lái)圖象的過(guò)程OnEraseBkgnd,它利用背景色填充窗體繪圖區(qū),然后在調(diào)用新的繪圖代碼進(jìn)行重繪,這樣一擦一寫造成了圖象顏色的反差。當(dāng)WM_PAINT的響應(yīng)很頻繁的時(shí)候,這種反差也就越發(fā)明顯。于是我們就看到了閃爍現(xiàn)象。

                我們會(huì)很自然的想到,避免背景色的填充是最直接的辦法。但是那樣的話,窗體上會(huì)變的一團(tuán)糟。因?yàn)槊看卫L制圖象的時(shí)候都沒有將原來(lái)的圖象清除,造成了圖象的殘留,于是窗體重繪時(shí),畫面往往會(huì)變的亂七八糟。所以單純的禁止背景重繪是不夠的。我們還要進(jìn)行重新繪圖,但要求速度很快,于是我們想到了使用BitBlt函數(shù)。它可以支持圖形塊的復(fù)制,速度很快。我們可以先在內(nèi)存中作圖,然后用此函數(shù)將做好的圖復(fù)制到前臺(tái),同時(shí)禁止背景刷新,這樣就消除了閃爍。以上也就是雙緩沖繪圖的基本的思路。

                一、普通方法:

                先按普通做圖的方法進(jìn)行編程。即在視類的OnDraw函數(shù)中添加繪圖代碼。在此我們繪制若干同心圓,代碼如下:
             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);

            }


                編譯運(yùn)行程序,嘗試改變窗口大小,可以發(fā)現(xiàn)閃爍現(xiàn)象。

                二、雙緩沖方法:

                在雙緩沖方法中,首先要做的是屏蔽背景刷新。背景刷新其實(shí)是在響應(yīng)WM_ERASEBKGND消息。我們?cè)谝曨愔刑砑訉?duì)這個(gè)消息的響應(yīng),可以看到缺省的代碼如下:
             BOOL CMYView::OnEraseBkgnd(CDC* pDC)

            {

            return CView::OnEraseBkgnd(pDC);

            }


                是調(diào)用父類的OnEraseBkgnd函數(shù),我們屏蔽此調(diào)用,只須直接return TRUE;即可。

                下面是內(nèi)存緩沖作圖的步驟。
             CPoint ptCenter;

            CRect rect,ellipseRect;

            GetClientRect(&rect);

            ptCenter = rect.CenterPoint();

            CDC dcMem; //用于緩沖作圖的內(nèi)存DC

            CBitmap bmp; //內(nèi)存中承載臨時(shí)圖象的位圖

            dcMem.CreateCompatibleDC(pDC); //依附窗口DC創(chuàng)建兼容內(nèi)存DC

            bmp.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//創(chuàng)建兼容位圖

            dcMem.SelectObject(&bmp); //將位圖選擇進(jìn)內(nèi)存DC

            //按原來(lái)背景填充客戶區(qū),不然會(huì)是黑色

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

            for(int i=20;i>0;i--) //在內(nè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);//將內(nèi)存DC上的圖象拷貝到前臺(tái)

            dcMem.DeleteDC(); //刪除DC

            bm.DeleteObject(); //刪除位圖


                由于復(fù)雜的畫圖操作轉(zhuǎn)入后臺(tái),我們看到的是速度很快的復(fù)制操作,自然也就消除了閃爍現(xiàn)象。
                注意:bmp.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());

                這里面CreateCompatibleBitmap第一個(gè)參數(shù)不能用dcMem,這樣的話創(chuàng)建的是黑白位圖。如果你要?jiǎng)?chuàng)建彩色位圖,需要用pDC,它用來(lái)創(chuàng)建了內(nèi)存DC. 詳細(xì)請(qǐng)見下面的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 );





            雙緩沖技術(shù)繪圖

              當(dāng)數(shù)據(jù)量很大時(shí),繪圖可能需要幾秒鐘甚至更長(zhǎng)的時(shí)間,而且有時(shí)還會(huì)出現(xiàn)閃爍現(xiàn)象,為了解決這些問(wèn)題,可采用雙緩沖技術(shù)來(lái)繪圖。
              雙緩沖即在內(nèi)存中創(chuàng)建一個(gè)與屏幕繪圖區(qū)域一致的對(duì)象,先將圖形繪制到內(nèi)存中的這個(gè)對(duì)象上,再一次性將這個(gè)對(duì)象上的圖形拷貝到屏幕上,這樣能大大加快繪圖的速度。雙緩沖實(shí)現(xiàn)過(guò)程如下:
              1、在內(nèi)存中創(chuàng)建與畫布一致的緩沖區(qū)
              2、在緩沖區(qū)畫圖
              3、將緩沖區(qū)位圖拷貝到當(dāng)前畫布上
              4、釋放內(nèi)存緩沖區(qū)
              在圖形圖象處理編程過(guò)程中,雙緩沖是一種基本的技術(shù)。我們知道,如果窗體在響應(yīng)WM_PAINT消息的時(shí)候要進(jìn)行復(fù)雜的圖形處理,那么窗體在重繪時(shí)由于過(guò)頻的刷新而引起閃爍現(xiàn)象。解決這一問(wèn)題的有效方法就是雙緩沖技術(shù)。因?yàn)榇绑w在刷新時(shí),總要有一個(gè)擦除原來(lái)圖象的過(guò)程OnEraseBkgnd,它利用背景色填充窗體繪圖區(qū),然后在調(diào)用新的繪圖代碼進(jìn)行重繪,這樣一擦一寫造成了圖象顏色的反差。當(dāng)WM_PAINT的響應(yīng)很頻繁的時(shí)候,這種反差也就越發(fā)明顯。于是我們就看到了閃爍現(xiàn)象。 
              我們會(huì)很自然的想到,避免背景色的填充是最直接的辦法。但是那樣的話,窗體上會(huì)變的一團(tuán)糟。因?yàn)槊看卫L制圖象的時(shí)候都沒有將原來(lái)的圖象清除,造 成了圖象的殘留,于是窗體重繪時(shí),畫面往往會(huì)變的亂七八糟。所以單純的禁止背景重繪是不夠的。我們還要進(jìn)行重新繪圖,但要求速度很快,于是我們想到了使用 BitBlt函數(shù)。它可以支持圖形塊的復(fù)制,速度很快。我們可以先在內(nèi)存中作圖,然后用此函數(shù)將做好的圖復(fù)制到前臺(tái),同時(shí)禁止背景刷新,這樣就消除了閃 爍。以上也就是雙緩沖繪圖的基本的思路。 
              首先給出實(shí)現(xiàn)的程序,然后再解釋,同樣是在OnDraw(CDC *pDC)中: 
              CDC MemDC; //首先定義一個(gè)顯示設(shè)備對(duì)象 
              CBitmap MemBitmap;//定義一個(gè)位圖對(duì)象 //隨后建立與屏幕顯示兼容的內(nèi)存顯示設(shè)備 MemDC.CreateCompatibleDC(NULL); //這時(shí)還不能繪圖,因?yàn)闆]有地方畫 ^_^ 
              //下面建立一個(gè)與屏幕顯示兼容的位圖,至于位圖的大小嘛,可以用窗口的大小,也可以自己定義
              (如:有滾動(dòng)條時(shí)就要大于當(dāng)前窗口的大小,在BitBlt時(shí)決定拷貝內(nèi)存的哪部分到屏幕上)
              MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight); //將位圖選入到內(nèi)存顯示設(shè)備中 //只有選入了位圖的內(nèi)存顯示設(shè)備才有地方繪圖,畫到指定的位圖上 
              CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap); //先用背景色將位圖清除干凈,這里我用的是白色作為背景 //你也可以用自己應(yīng)該用的顏色 
              MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255)); //繪圖 
              MemDC.MoveTo(……); MemDC.LineTo(……); //將內(nèi)存中的圖拷貝到屏幕上進(jìn)行顯示 
              pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY); //繪圖完成后的清理 //把前面的pOldBit選回來(lái).在刪除MemBitmap之前要先從設(shè)備中移除它 
              MemDC.SelectObject(pOldBit); MemBitmap.DeleteObject(); MemDC.DeleteDC(); 雙緩沖(two way soft-closing)
            posted @ 2011-03-01 16:09 wrh 閱讀(6607) | 評(píng)論 (1)編輯 收藏

            4 畫圖
            在Windows中,繪圖一般在視圖窗口的客戶區(qū)進(jìn)行,使用的是設(shè)備上下文類CDC中各種繪圖函數(shù)。
            1. 映射模式與坐標(biāo)系
            1)默認(rèn)映射模式
            映射模式(map mode)影響所有的圖形和文本繪制函數(shù),它定義(將邏輯單位轉(zhuǎn)換為設(shè)備單位所使用的)度量單位和坐標(biāo)方向,Windows總是用邏輯單位來(lái)繪圖。
            缺省情況下,繪圖的默認(rèn)映射模式為MM_TEXT,其繪圖單位為像素(只要不打印輸出,屏幕繪圖使用該模式就夠了)。若窗口客戶區(qū)的寬和高分別為w和h像素,則其x坐標(biāo)是從左到右,范圍為0 ~ w-1;y坐標(biāo)是從上到下,范圍為0 ~ h-1。

            2)設(shè)置映射模式
            可以使用CDC類的成員函數(shù)GetMapMode和SetMapMode來(lái)獲得和設(shè)置當(dāng)前的映射模式:
            int GetMapMode( ) const; // 返回當(dāng)前的映射模式
            virtual int SetMapMode( int nMapMode ); // 返回先前的映射模式


            映射模式的nMapMode取值與含義
            符號(hào)常量 數(shù)字常量 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的向下,其余的都是向上,與數(shù)學(xué)上一致。除了MM_ANISOTROPIC外,其他所有映射模式的x與y方向的單位都是相同的。所有映射模式的邏輯坐標(biāo)的原點(diǎn)(0, 0)最初都是在窗口的左上角,但在CScrollView的派生類中,MFC會(huì)隨用戶滾動(dòng)文檔而自動(dòng)調(diào)整邏輯原點(diǎn)的相對(duì)位置(改變視點(diǎn)的原點(diǎn)屬性)。
            3)自定義映射模式
            自定義映射模式MM_ISOTROPIC(各向同性,x與y方向的單位必須相同)和MM_ANISOTROPIC(各向異性,x與y方向的單位可以不同)的單位和方向,可以通過(guò)用CDC類的成員函數(shù)G/SetWindowExt和G/SetViewportExt來(lái)獲取/設(shè)置窗口和視口的大小來(lái)確定:
            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類的成員函數(shù)SetViewportOrg來(lái)設(shè)置坐標(biāo)原點(diǎn)的位置:
            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));

            }
            將當(dāng)前的映射模式設(shè)置為各向異性自定義映射模式,窗口大小為1000個(gè)邏輯單位寬和1000個(gè)邏輯單位高,視口大小同當(dāng)前客戶區(qū),視口的坐標(biāo)原點(diǎn)設(shè)置在當(dāng)前客戶區(qū)的中央。由于使用了負(fù)數(shù)作為SetViewportExt函數(shù)的第2個(gè)參數(shù),所以y軸方向是向上的。

            可見,圓被畫成了橢圓,x與y方向上的邏輯單位不相同。
            4)單位轉(zhuǎn)換
            對(duì)所有非MM_TEXT映射模式,有如下重要規(guī)則:
            <!--[if !supportLists]-->l         <!--[endif]-->CDC的成員函數(shù)(如各種繪圖函數(shù))具有邏輯坐標(biāo)參數(shù)

            <!--[if !supportLists]-->l         <!--[endif]-->CWnd的成員函數(shù)(如各種響應(yīng)函數(shù))具有設(shè)備坐標(biāo)參數(shù)(如鼠標(biāo)位置point)

            <!--[if !supportLists]-->l         <!--[endif]-->位置的測(cè)試操作(如CRect的PtInRect函數(shù))只有使用設(shè)備坐標(biāo)時(shí)才有效

            <!--[if !supportLists]-->l         <!--[endif]-->長(zhǎng)期使用的值應(yīng)該用邏輯坐標(biāo)保存(如窗口滾動(dòng)后保存的設(shè)備坐標(biāo)就無(wú)效了)


            因此,為了使應(yīng)用程序能夠正確工作,除MM_TEXT映射模式外,其他映射模式都需要進(jìn)行單位轉(zhuǎn)換。下面是邏輯單位到設(shè)備單位(如像素)的轉(zhuǎn)換公式:
            x比例因子 = 視口寬度 / 窗口寬度
            y比例因子 = 視口高度 / 窗口高度
            設(shè)備x = 邏輯x * x比例因子 + x原點(diǎn)偏移量
            設(shè)備y = 邏輯y * y比例因子 + y原點(diǎn)偏移量
            Windows的GDI負(fù)責(zé)邏輯坐標(biāo)和設(shè)備坐標(biāo)之間的轉(zhuǎn)換,這可以調(diào)用CDC類的成員函數(shù)LPtoDP和DPtoLP來(lái)進(jìn)行:
            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; // 邏輯坐標(biāo)

                          CClientDC dc(this);
                          dc.SetMapMode(MM_LOENGLISH);
                          dc.LPtoDP(rect); // 轉(zhuǎn)化成設(shè)備坐標(biāo)
                   if (rect.PtInRect(point)) // 位置的測(cè)試操作只有使用設(shè)備坐標(biāo)時(shí)才有效

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

                   float t,y;

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

            pDC->SetMapMode(MM_HIMETRIC);
                          pDC->DPtoLP(&point); // 轉(zhuǎn)化成邏輯坐標(biāo)
            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. 畫像素點(diǎn)
            畫像素點(diǎn)就是設(shè)置像素點(diǎn)的顏色,從前面3)(2)已知道這可由CDC的成員函數(shù)SetPixel來(lái)做,該函數(shù)的原型為:
            COLORREF SetPixel( int x, int y, COLORREF crColor ); 或

            COLORREF SetPixel( POINT point, COLORREF crColor );

            其中,x與y分別為像素點(diǎn)的橫坐標(biāo)與縱坐標(biāo),crColor為像素的顏色值。例如
            pDC->SetPixel(i, j, RGB(r, g, b));

            3.畫線狀圖
            在Windows中,線狀圖必須用筆來(lái)畫(筆的創(chuàng)建與使用見前面的3)(3)),下面是CDC類中可以繪制線狀圖的常用成員函數(shù):
            <!--[if !supportLists]-->l         <!--[endif]-->當(dāng)前位置:設(shè)置當(dāng)前位置為(x, y)或point:(返回值為原當(dāng)前位置的坐標(biāo))

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

            <!--[if !supportLists]-->l         <!--[endif]-->畫線:使用DC中的筆從當(dāng)前位置畫線到點(diǎn)(x, y)或point:(若成功返回非0值):

            BOOL LineTo( int x, int y ); 或BOOL LineTo( POINT point );
            <!--[if !supportLists]-->l         <!--[endif]-->畫折線:使用DC中的筆,依次將點(diǎn)數(shù)組lpPoints中的nCount(≥2)個(gè)點(diǎn)連接起來(lái),形成一條折線:

            BOOL Polyline( LPPOINT lpPoints, int nCount );
            <!--[if !supportLists]-->l         <!--[endif]-->畫多邊形:似畫折線,但還會(huì)將最后的點(diǎn)與第一個(gè)點(diǎn)相連形成多邊形,并用DC中的刷填充其內(nèi)部區(qū)域:

            BOOL Polygon( LPPOINT lpPoints, int nCount );
            <!--[if !supportLists]-->l         <!--[endif]-->畫矩形:使用DC中的筆畫左上角為(x1, y1)、右下角為(x2, y2)或范圍為*lpRect的矩形的邊線,并用DC中的刷填充其內(nèi)部區(qū)域:

            BOOL Rectangle( int x1, int y1, int x2, int y2 ); 或
            BOOL Rectangle( LPCRECT lpRect );
                          有時(shí)需要根據(jù)用戶給定的兩個(gè)任意點(diǎn)來(lái)重新構(gòu)造左上角和右下角的點(diǎn),例如:
                          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矩形的內(nèi)接橢圓倒角,再用DC中的刷填充其內(nèi)部區(qū)域:

            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的矩形中畫內(nèi)接(橢)圓的邊線,并用DC中的刷填充其內(nèi)部區(qū)域:

            BOOL Ellipse( int x1, int y1, int x2, int y2 );
            BOOL Ellipse( LPCRECT lpRect );
            注意,CDC中沒有畫圓的專用函數(shù)。在這里,圓是作為橢圓的(寬高相等)特例來(lái)畫的。
            <!--[if !supportLists]-->l         <!--[endif]-->畫弧:(x1, y1)與(x2, y2)或lpRect的含義同畫(橢)圓,(x3, y3)或ptStart為弧的起點(diǎn),(x4, y4)或ptEnd為弧的終點(diǎn):(逆時(shí)針方向旋轉(zhuǎn))

            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]-->畫弓弦:參數(shù)的含義同上,只是用一根弦連接弧的起點(diǎn)和終點(diǎn),形成一個(gè)弓形,并用DC中的刷填充其內(nèi)部區(qū)域:

            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中,面狀圖必須用刷來(lái)填充(刷的創(chuàng)建與使用見前面的3)(4))。上面(2)中的Polygon、Rectangle、Ellipse和Chord等畫閉合線狀圖的函數(shù),只要DC中的刷不是空刷,都可以用來(lái)畫對(duì)應(yīng)的面狀圖(邊線用當(dāng)前筆畫,內(nèi)部用當(dāng)前刷填充)。下面介紹的是CDC類中只能繪制面狀圖的其他常用成員函數(shù):
            <!--[if !supportLists]-->l         <!--[endif]-->畫填充矩形:用指定的刷pBrush畫一個(gè)以lpRect為區(qū)域的填充矩形,無(wú)邊線,填充區(qū)域包括矩形的左邊界和上邊界,但不包括矩形的右邊界和下邊界:

            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]-->畫餅圖(扇形):參數(shù)含義同Arc,但將起點(diǎn)和終點(diǎn)都與外接矩形的中心相連接,形成一個(gè)扇形區(qū)域,用DC中的刷填充整個(gè)扇形區(qū)域,無(wú)另外的邊線:

            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]-->畫拖動(dòng)的矩形:先擦除線寬為sizeLast、填充刷為pBrushLast的原矩形lpRectLast,然后再以線寬為size、填充刷為pBrush畫新矩形lpRectLast。矩形的邊框用灰色的點(diǎn)虛線畫,缺省的填充刷為空刷:

            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]-->填充區(qū)域:

            <!--[if !supportLists]-->n     <!--[endif]-->用當(dāng)前刷從點(diǎn)(x, y)開始向四周填充到顏色為crColor的邊界:

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

            <!--[if !supportLists]-->n     <!--[endif]-->用當(dāng)前刷從點(diǎn)(x, y)開始向四周填充:

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

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

            <!--[if !supportLists]-->u <!--[endif]-->nFillType = FLOODFILLBORDER:填充到顏色為crColor的邊界(同F(xiàn)loodFill);(用于填充內(nèi)部顏色不同但邊界顏色相同的區(qū)域)
            <!--[if !supportLists]-->u <!--[endif]-->nFillType = FLOODFILLSURFACE:填充所有顏色為crColor的點(diǎn),直到碰到非crColor顏色的點(diǎn)為止。(點(diǎn)(x, y)的顏色也必須為crColor),(用于填充內(nèi)部顏色相同但邊界顏色可以不同的區(qū)域)。例如:
            pDC->ExtFloodFill(point.x, point.y, pDC->GetPixel(point), FLOODFILLSURFACE);

            5.清屏
            Windows沒有提供專門的清屏函數(shù),可以調(diào)用CWnd的下面兩個(gè)函數(shù)調(diào)用來(lái)完成該功能:
            void Invalidate(BOOL bErase = TRUE);
            void UpdateWindow( );
            或調(diào)用CWnd的函數(shù)
            BOOL RedrawWindow(
               LPCRECT lpRectUpdate = NULL,

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

            );
            來(lái)完成。
            例如(菜單項(xiàng)ID_CLEAR的事件處理函數(shù)):
            CDrawView::OnClear() { // 調(diào)用OnDraw來(lái)清屏
                   //Invalidate();
                   //UpdateWindow( );
                   RedrawWindow( );
            }
            也可以用畫填充背景色矩形的方法來(lái)清屏,如:
                   RECT rect;
                   GetClientRect(&rect);
                   pDC->FillSolidRect(&rect, RGB(255, 255, 255));

            6.在控件上繪圖
            可以在對(duì)話框資源中放置圖片控件,并對(duì)其類型屬性選Frame。可在對(duì)話框的繪圖消息響應(yīng)函數(shù)OnPaint或其他函數(shù)中,用CWnd類的函數(shù)GetDlgItem:
            CWnd* GetDlgItem( int nID ) const;
            來(lái)獲得圖片控件的窗口對(duì)象,再用函數(shù)GetDC:
            CDC* GetDC( );

            由窗口對(duì)象得到DC,然后就可以用該DC在控件中畫圖。如(在ID為IDC_HUESAT的圖片控件上畫調(diào)色板)
            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); // 自定義函數(shù),見網(wǎng)絡(luò)硬盤的

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

                                 }
                          ... ...
                   }
            }
            在非Frame類靜態(tài)控件上繪圖,必須先按順序依次調(diào)用CWnd類的Invalidate和UpdateWindow函數(shù)后,再開始用DC畫圖。如在一個(gè)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));
            }

            若干說(shuō)明:
            <!--[if !supportLists]-->l     <!--[endif]-->除了基于對(duì)話框的程序外,其他對(duì)話框類都需要自己添加(重寫型)消息響應(yīng)函數(shù)OnInitDialog,來(lái)做一些必要的初始化對(duì)話框的工作。添加方法是:先在項(xiàng)目區(qū)選中“類視圖”頁(yè),再選中對(duì)應(yīng)的對(duì)話框類,然后在屬性窗口的“重寫”頁(yè)中添加該函數(shù);

            <!--[if !supportLists]-->l     <!--[endif]-->為了使在運(yùn)行時(shí)能夠不斷及時(shí)更新控件的顯示(主要是自己加的顯式代碼),可以將自己繪制控件的所有代碼都全部加入對(duì)話框類的消息響應(yīng)函數(shù)OnPaint中。在需要時(shí)(例如在繪圖參數(shù)修改后),自己調(diào)用CWnd的Invalidate和UpdateWindow函數(shù),請(qǐng)求系統(tǒng)刷新對(duì)話框和控件的顯示。因?yàn)榭丶彩谴翱冢丶惗际荂Wnd的派生類。所以在對(duì)話框和控件中,可以像在視圖類中一樣,調(diào)用各種CWnd的成員函數(shù)。

            <!--[if !supportLists]-->l     <!--[endif]-->一般的對(duì)話框類,缺省時(shí)都沒有明寫出OnPaint函數(shù)。可以自己在對(duì)話框類中添加WM_PAINT消息的響應(yīng)函數(shù)OnPaint來(lái)進(jìn)行一些繪圖工作。

            <!--[if !supportLists]-->l     <!--[endif]-->為了在鼠標(biāo)指向按鈕時(shí),讓按鈕上自己繪制的圖形不被消去,可以設(shè)置按鈕控件的“Owner Draw”屬性為“True”。

            <!--[if !supportLists]-->l     <!--[endif]-->如果希望非按鈕控件(如圖片控件和靜態(tài)文本等),也可以響應(yīng)鼠標(biāo)消息(如單擊、雙擊等),需要設(shè)置控件的“Notify”屬性為“True”。

            <!--[if !supportLists]-->l     <!--[endif]-->使用OnPaint函數(shù)在對(duì)話框客戶區(qū)的空白處(無(wú)控件的地方)繪制自己的圖形,必須屏蔽掉其中缺省的對(duì)對(duì)話框基類的OnPaint函數(shù)的調(diào)用:

            //CDialog::OnPaint();
            <!--[if !supportLists]-->l     <!--[endif]-->對(duì)話框的背景色,可以用CWnd類的成員函數(shù):

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

            下面是部分例子代碼:(其中FillColor和ShowImg為自定義的成員函數(shù))
            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: 在此處添加消息處理程序代碼
                   // 不為繪圖消息調(diào)用 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 設(shè)置繪圖屬性
            除了映射模式外,還有許多繪圖屬性可以設(shè)置,如背景、繪圖方式、多邊形填充方式、畫弧方向、刷原點(diǎn)等。
            1.背景
            1)背景色
            當(dāng)背景模式為不透明時(shí),背景色決定線狀圖的空隙顏色(如虛線中的空隙、條紋刷的空隙和文字的空隙),可以使用CDC類的成員函數(shù)GetBkColor和SetBkColor來(lái)獲得和設(shè)置當(dāng)前的背景顏色:
            COLORREF GetBkColor( ) const; // 返回當(dāng)前的背景色
            virtual COLORREF SetBkColor( COLORREF crColor ); // 返回先前的背景色
                                                                                          // 若出錯(cuò)返回0x80000000
            2)背景模式
            背景模式影響有空隙的線狀圖的空隙(如虛線中的空隙、條紋刷的空隙和文字的空隙)用什么辦法填充。可以使用CDC類的成員函數(shù)GetBkMode和SetBkMode來(lái)獲得和設(shè)置當(dāng)前的背景模式:
            int GetBkMode( ) const; // 返回當(dāng)前背景模式
            int SetBkMode( int nBkMode ); // 返回先前背景模式
            背景模式的取值
            nBkMode值 名稱 作用 
            OPAQUE 不透明的(缺省值) 空隙用背景色填充 
            TRANSPARENT 透明的 空隙處保持原背景圖不變

            2. 繪圖模式
            繪圖模式(drawing mode)指前景色的混合方式,它決定新畫圖的筆和刷的顏色(pbCol)如何與原有圖的顏色(scCol)相結(jié)合而得到結(jié)果像素色(pixel)。
            1)設(shè)置繪圖模式
            可使用CDC類的成員函數(shù)SetROP2 (ROP = Raster OPeration光柵操作)來(lái)設(shè)置繪圖模式:
            int SetROP2( int nDrawMode );

            其中,nDrawMode可取值:
            繪圖模式nDrawMode的取值
            符號(hào)常量 作用 運(yùn)算結(jié)果 
            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)畫移動(dòng)圖形
            為了能畫移動(dòng)的位置標(biāo)識(shí)(如十字、一字)和隨鼠標(biāo)移動(dòng)畫動(dòng)態(tài)圖形(如直線、矩形、橢圓),必須在不破壞原有背景圖形的基礎(chǔ)上移動(dòng)這些圖形。
            移動(dòng)圖形采用的是異或畫圖方法,移動(dòng)圖形的過(guò)程為:異或畫圖、在原位置再異或化圖(擦除)、在新位置異或畫圖、……。

                   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;
            較完整的拖放動(dòng)態(tài)畫圖的例子,可參照下面的“3. 拖放畫動(dòng)態(tài)直線”部分。
            3)其他屬性
            <!--[if !supportLists]-->l         <!--[endif]-->多邊形填充方式:可使用CDC類的成員函數(shù)GetPolyFillMode和SetPolyFillMode來(lái)確定多邊形的填充方式:

            int GetPolyFillMode( ) const;
            int SetPolyFillMode( int nPolyFillMode );
            其中nPolyFillMode 可取值A(chǔ)LTERNATE(交替——填充奇數(shù)邊和偶數(shù)邊之間的區(qū)域,缺省值)或WINDING(纏繞——根據(jù)多邊形邊的走向來(lái)確定是否填充一區(qū)域)
            <!--[if !supportLists]-->l         <!--[endif]-->畫弧方向:可使用CDC類的成員函數(shù)GetArcDirection和SetArcDirection來(lái)確定Arc、Chord、Pie等函數(shù)的畫弧方向:

            int GetArcDirection( ) const;
            int SetArcDirection( int nArcDirection );
            其中,nArcDirection可取值A(chǔ)D_COUNTERCLOCKWISE(逆時(shí)針方向,缺省值)和AD_CLOCKWISE(順時(shí)針方向)
            <!--[if !supportLists]-->l         <!--[endif]-->刷原點(diǎn):可使用CDC類的成員函數(shù)GetBrushOrg和SetBrushOrg來(lái)確定可填充繪圖函數(shù)的條紋或圖案刷的起點(diǎn):(缺省值為客戶區(qū)左上角的坐標(biāo)原點(diǎn)(0, 0))

            CPoint GetBrushOrg( ) const;
            CPoint SetBrushOrg( int x, int y );
            CPoint SetBrushOrg( POINT point );
            3.拖放畫動(dòng)態(tài)直線
            下面是一個(gè)較完整的拖放動(dòng)態(tài)畫直線的例子:
            // 類變量
            class CDrawView : public CView { 
                   //……
            protected:
                   BOOL m_bLButtonDown, m_bErase; // 判斷是否按下左鼠標(biāo)鍵

            //和是否需要擦除圖形的類變量
                   CPoint p0, pm; // 記錄直線起點(diǎn)和動(dòng)態(tài)終點(diǎn)的類變量

                   CPen * pGrayPen, * pLinePen; // 定義灰色和直線筆

                   //……
            }
            // 構(gòu)造函數(shù)
            CDrawView::CDrawView() {
                   m_bLButtonDown = FALSE; // 設(shè)左鼠標(biāo)鍵按下為假

                   m_bErase = FALSE; // 設(shè)需要擦除為假

                   pGrayPen = new CPen(PS_SOLID, 0, RGB(128, 128, 128));// 創(chuàng)建灰色筆

                   pLinePen = new CPen(PS_SOLID, 0, RGB(255, 0, 0));// 創(chuàng)建紅色的直線筆

            }
            // 鼠標(biāo)消息響應(yīng)函數(shù)
            void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) {

                   m_bLButtonDown = TRUE; // 設(shè)左鼠標(biāo)鍵按下為真

                   SetCapture(); // 設(shè)置鼠標(biāo)捕獲

                   // SetCursor(LoadCursor(NULL, IDC_CROSS)); // 設(shè)置鼠標(biāo)為十字

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

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

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

                   SetCursor(LoadCursor(NULL, IDC_CROSS)); // 設(shè)置鼠標(biāo)為十字

                   if (m_bLButtonDown) { // 左鼠標(biāo)鍵按下為真

                          CDC* pDC = GetDC(); // 獲取設(shè)備上下文

                          pDC->SelectObject(pGrayPen);// 選取灰色筆
                          pDC->SetROP2(R2_XORPEN);// 設(shè)置為異或繪圖方式
                          if (m_bErase) { // 需要擦除為真

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

                          }
                          else // 需要擦除為假

                                 m_bErase = TRUE; // 設(shè)需要擦除為真
                          pDC->MoveTo(p0); pDC->LineTo(point); // 繪制新直線

                          pm = point; // 記錄老終點(diǎn)

                          ReleaseDC(pDC); // 釋放設(shè)備上下文

                   }
                   CView::OnMouseMove(nFlags, point);

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

                   ReleaseCapture(); // 釋放鼠標(biāo)捕獲

                   if (m_bLButtonDown) { // 左鼠標(biāo)鍵按下為真

                          CDC* pDC = GetDC(); // 獲取設(shè)備上下文

                          pDC->SelectObject(pGrayPen);// 選取灰色筆
                          pDC->SetROP2(R2_XORPEN); // 設(shè)置為異或繪圖方式
                          pDC->MoveTo(p0); pDC->LineTo(pm); // 擦除原直線

                          pDC->SelectObject(pLinePen); // 選擇直線筆
                          pDC->SetROP2(R2_COPYPEN);// 設(shè)置為覆蓋繪圖方式
                          pDC->MoveTo(p0); pDC->LineTo(point); // 繪制最終的直線

                          m_bLButtonDown = FALSE; // 重設(shè)左鼠標(biāo)鍵按下為假

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

                          ReleaseDC(pDC); // 釋放設(shè)備上下文

                   }
                   CView::OnLButtonUp(nFlags, point);

            }

            posted @ 2011-03-01 14:03 wrh 閱讀(2445) | 評(píng)論 (0)編輯 收藏
            char是C語(yǔ)言標(biāo)準(zhǔn)數(shù)據(jù)類型,字符型,至于由幾個(gè)字節(jié)組成通常由編譯器決定,一般一個(gè)字節(jié)。

            Windows為了消除各編譯器的差別,重新定義了一些數(shù)據(jù)類型,你提到了另外幾個(gè)類型都是這樣。CHAR為

            單字節(jié)字符。還有個(gè)WCHAR為Unicode字符,即不論中英文,每個(gè)字有兩個(gè)字節(jié)組成。如果當(dāng)前編譯方式為

            ANSI(默認(rèn))方式,TCHAR等價(jià)于CHAR,如果為Unicode方式,TCHAR等價(jià)于WCHAR。在當(dāng)前版本LPCSTR和

            LPSTR沒區(qū)別,即以零結(jié)尾的字符串指針,相當(dāng)于CHAR *。 LPSTR、LPCSTR相當(dāng)于char *,所以這種類型

            變量的賦值等同于char *的賦值
            下面給出兩個(gè)例子,一個(gè)是直接賦值,另一個(gè)是間接的。
                Ex1: LPSTR lpstrMsg = "I'm tired.";
                Ex2: char strMsg[]="I'm tired.";
                LPSTR lpstrMsg = (LPSTR) strMsg

            posted @ 2011-02-17 09:14 wrh 閱讀(586) | 評(píng)論 (0)編輯 收藏

            [原創(chuàng)]在VC中徹底玩轉(zhuǎn)Excel
                如今Excel是越來(lái)越重要了,在我們自己開發(fā)的程序中不免要和Excel打交道了。利用Automation技術(shù),我們可以在不去了解
            數(shù)據(jù)庫(kù)的情況下玩轉(zhuǎn)Excel,而且你會(huì)發(fā)現(xiàn)一切竟如此輕松!
                好了,咱們開始吧,我不喜歡用長(zhǎng)篇累牘的代碼來(lái)故弄玄虛,所以下面的代碼都是切中要害的片段,總體上是個(gè)連貫的過(guò)程,
            包括啟動(dòng)Excel,讀取數(shù)據(jù),寫入數(shù)據(jù),以及最后的關(guān)閉Excel,其中還包括了很多人感興趣的合并單元格的處理。
                特別說(shuō)明以下代碼需要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的動(dòng)態(tài)連接庫(kù)
             if(!AfxOleInit()) 
             {
                    AfxMessageBox("無(wú)法初始化COM的動(dòng)態(tài)連接庫(kù)!");
                    return ;
                 }  
             
             
             //*****
             //創(chuàng)建Excel 2000服務(wù)器(啟動(dòng)Excel)
             if(!app.CreateDispatch("Excel.Application"))
             {
              AfxMessageBox("無(wú)法啟動(dòng)Excel服務(wù)器!");
                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());
             
             
             //*****
             //得到當(dāng)前活躍sheet
             //如果有單元格正處于編輯狀態(tài)中,此操作不能返回,會(huì)一直等待
             lpDisp=book.GetActiveSheet();
             sheet.AttachDispatch(lpDisp);
             

             //*****
             //讀取已經(jīng)使用區(qū)域的信息,包括已經(jīng)使用的行數(shù)、列數(shù)、起始行、起始列
             Range usedRange;
             usedRange.AttachDispatch(sheet.GetUsedRange());
             range.AttachDispatch(usedRange.GetRows());
             long iRowNum=range.GetCount();                   //已經(jīng)使用的行數(shù)
             
             range.AttachDispatch(usedRange.GetColumns());
             long iColNum=range.GetCount();                   //已經(jīng)使用的列數(shù)
             
             long iStartRow=usedRange.GetRow();               //已使用區(qū)域的起始行,從1開始
             long iStartCol=usedRange.GetColumn();            //已使用區(qū)域的起始列,從1開始
             
             
             //*****
             //讀取第一個(gè)單元格的值
             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字節(jié)的數(shù)字
             {
              str.Format("%f",vResult.dblVal);
             }
             else if(vResult.vt==VT_DATE)    //時(shí)間格式
             {
              SYSTEMTIME st;
                 VariantTimeToSystemTime(&vResult.date, &st);
             }
             else if(vResult.vt==VT_EMPTY)   //單元格空的
             {
              str="";
             } 
             
             
             //*****
             //讀取第一個(gè)單元格的對(duì)齊方式,數(shù)據(jù)類型:VT_I4
             //讀取水平對(duì)齊方式
             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:      //默認(rèn)
               break;
              case -4108:  //居中
               break;
              case -4131 : //靠左
               break;
              case -4152 : //靠右
               break;
              }
             
             }
             
             //垂直對(duì)齊方式
             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;
              }
             
             }
             
             
             //*****
             //設(shè)置第一個(gè)單元格的值"HI,EXCEL!"
             range.SetItem(COleVariant(1),COleVariant(1),COleVariant("HI,EXCEL!"));
             

             //*****
             //設(shè)置第一個(gè)單元格字體顏色:紅色
             Font font;
             range.AttachDispatch(sheet.GetCells());
             range.AttachDispatch((range.GetItem (COleVariant(long(1)), COleVariant(long(1)))).pdispVal);
             font.SetColor(COleVariant((long)0xFF0000)); 
             
             
             //*****
             //合并單元格的處理
             //包括判斷第一個(gè)單元格是否為合并單元格,以及將第一個(gè)單元格進(jìn)行合并
             Range unionRange;
             range.AttachDispatch(sheet.GetCells());
             unionRange.AttachDispatch(range.GetItem (COleVariant((long)1),COleVariant((long)1)).pdispVal );
             
             vResult=unionRange.GetMergeCells();   
             if(vResult.boolVal==-1)             //是合并的單元格   
             {
              //合并單元格的行數(shù)
              range.AttachDispatch (unionRange.GetRows ());
              long iUnionRowNum=range.GetCount ();
              
              //合并單元格的列數(shù)
              range.AttachDispatch (unionRange.GetColumns ());
              long iUnionColumnNum=range.GetCount ();  
             
              //合并區(qū)域的起始行,列
              long iUnionStartRow=unionRange.GetRow();       //起始行,從1開始
              long iUnionStartCol=unionRange.GetColumn();    //起始列,從1開始
             
             }
             else if(vResult.boolVal==0)  
             {//不是合并的單元格}
             
             //將第一個(gè)單元格合并成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); 
             
             
             //*****
             //關(guān)閉所有的book,退出Excel
             book.Close (covOptional,COleVariant(OutFilename),covOptional);
             books.Close();     
             app.Quit();    

            關(guān)于excel.h和excel.cpp,要注意版本問(wèn)題.
            比如對(duì)excel xp, 類庫(kù)是直接包含在excel.exe中. 因此你只要用加入類(add class)的方法,直接選中excel.exe,并選擇對(duì)話框中的常用的幾個(gè)類(如Rang)就可以編程了. 千萬(wàn)不要選所有的類,否則太大了.

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


            修改字體顏色的那段漏了一句,應(yīng)為:
              //*****
              //設(shè)置第一個(gè)單元格字體顏色:紅色
              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的計(jì)算機(jī)上,也可能安裝在XP的機(jī)子上,有的用戶還用97,(指的都是office),可能會(huì)出現(xiàn)版本問(wèn)題------其中比較麻煩的是--在app.quit()后,仍舊存在excel進(jìn)程,如果這樣?怎么解決?
            作者:zhengkuo 2003-12-2 10:51:50)


            請(qǐng)教高手,能否用vb控制excel做成dll,在arx中進(jìn)行調(diào)用,因?yàn)楫吘箆b與excel親切
            作者:unionsoft 2004-1-30 11:06:58)

             

            以下是引用zhengkuo在2003-12-2 10:50:00的發(fā)言:
            如果,程序既要安裝在2000的計(jì)算機(jī)上,也可能安裝在XP的機(jī)子上,有的用戶還用97,(指的都是office),可能會(huì)出現(xiàn)版本問(wèn)題------其中比較麻煩的是--在app.quit()后,仍舊存在excel進(jìn)程,如果這樣?怎么解決?
            兼容性問(wèn)題:

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

            Excel程序不能退出的問(wèn)題:
                  1 . 不要使用#import導(dǎo)入類型庫(kù),如:#import "c:\\excel\\excel.olb"
                  2 . 程序結(jié)束時(shí),確保所有IDispatch都釋放了,如:app.ReleaseDispatch (); 

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


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


            有版本問(wèn)題時(shí),可以加一個(gè)判斷:1、首先通過(guò)  exlApp得到版本,比如9.0,10.0,11.0,10.0以后的版本注意open函數(shù)的參數(shù)為15個(gè),即最后在增加兩個(gè)covOptional,即可,另外,補(bǔ)充一下,判斷當(dāng)前是否有excel應(yīng)用程序在運(yùn)行,使之更舒服一些:
            ::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的發(fā)言:
            請(qǐng)問(wèn)各位高手,從哪里得onclick=Cswf() height=22 alt=Flash圖片 src="skins/default/ubb/swf.gif" width=23 border=0>到Excel.cp...


            Excel.cpp和Excel.h是從Excel的類型庫(kù)中獲取的,類型庫(kù)類似C++中的頭文件,包括接口,方法,屬性的定義;類型庫(kù)在Excel的安裝目錄可以找到,Excel的版本不同,這個(gè)類型庫(kù)也不一樣,如下所示:
            Excel 95 and prior   :   xl5en32.olb
             Excel 97             :   excel8.olb
             Excel 2000           :   excel9.olb
             Excel 2002           :   excel.exe

            具體的獲取方法:

            1 . 使用VC++新建立一個(gè)基于MFC的EXE工程

            2 . 點(diǎn)擊菜單"查看"-->"建立類向?qū)?,此時(shí)會(huì)彈全"MFC ClassWizard"對(duì)話框

            3 . 點(diǎn)擊"Add Class"-->"From a type libray",指定Excel的type libray,在Excel的安裝目錄下可以找到,如:"D:\\Microsoft Office\\Office\\EXCEL9.OLB"

            4 . 在彈出的對(duì)話框中選擇所需的類,按"確定",Excel.cpp和Excel.h就產(chǎn)生了。

            作者: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 );   

            編譯顯示錯(cuò)誤:

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

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


            請(qǐng)問(wèn)怎樣才能增加一個(gè)工作表?
            作者:夢(mèng)幻神話 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);
             }

            請(qǐng)教:為什么str得到的結(jié)果都是1×1?(求合并單元格的原始行數(shù)和列數(shù))。

            謝謝。。。。。。。。。。

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


            首先你的Cells(2,2)是否處于合并單元格中
            其次,你缺少了個(gè)關(guān)鍵語(yǔ)句:iCell.GetMergeArea()),你可以參考下面的語(yǔ)句

            Range UnionRange;

            UnionRange.AttachDispatch(iCell.GetMergeArea());  //先要獲取合并區(qū)域

            range.AttachDispatch (UnionRange.GetRows ());

            long iRowNum = range.GetCount();             //合并單元格行數(shù)

            range.AttachDispatch (UnionRange.GetColumns ());

            long iColNum = Range.GetCount();            //合并單元格列數(shù)

               

            我就想在excel里 實(shí)現(xiàn),第一行為標(biāo)題(居中)
            第二行為時(shí)間(左對(duì)齊),下面為9列數(shù)據(jù).最后一行為簽名(左對(duì)齊)
            怎么辦?  回復(fù)  更多評(píng)論
             
            # re: [轉(zhuǎn)]在VC中徹底玩轉(zhuǎn)Excel
            2006-11-26 17:24 | 好學(xué)者

            修改字體顏色的那段漏了一句,應(yīng)為:
            //*****
            //設(shè)置第一個(gè)單元格字體顏色:紅色
            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是哪兒來(lái)的哦!  回復(fù)  更多評(píng)論
             
            # re: [轉(zhuǎn)]在VC中徹底玩轉(zhuǎn)Excel
            2007-10-19 15:11 | 唐特

            Font 和 _Application,Workbooks等等這些東西一樣, 是打開excel.exe(excel9.olb) 的時(shí)候添加的class  回復(fù)  更多評(píng)論
             
            # re: [轉(zhuǎn)]在VC中徹底玩轉(zhuǎn)Excel
            2008-01-16 11:19 | HU

            請(qǐng)教一個(gè)問(wèn)題,現(xiàn)在出現(xiàn)了OFFICE 2007,在2000中做的程序,在2007下無(wú)法啟動(dòng)EXCEL服務(wù),就是CreateDispatch失敗。
            但是,運(yùn)行的環(huán)境既有2007,也會(huì)有老版本的EXCEL,這種情況下,怎么辦?怎么讓程序更有通用性?謝謝!!!!  回復(fù)  更多評(píng)論
             
            # re: [轉(zhuǎn)]在VC中徹底玩轉(zhuǎn)Excel
            2008-01-25 10:55 | 想飛的星期

            請(qǐng)問(wèn)怎么知道我createdispatch出來(lái)的excel已經(jīng)關(guān)閉了??我還想每次重用同一個(gè)excel進(jìn)程,怎么辦?  回復(fù)  更多評(píng)論
             
            # re: [轉(zhuǎn)]在VC中徹底玩轉(zhuǎn)Excel
            2008-03-28 14:56 | lamorak

            你好,請(qǐng)教一下,我在做數(shù)據(jù)導(dǎo)入到excel中的程序,將3個(gè)文件的數(shù)據(jù)導(dǎo)入沒有問(wèn)題,我為了做測(cè)試,將這3個(gè)文件復(fù)制了44次,就是145個(gè)文件的數(shù)據(jù),總共有6702314行,200M的數(shù)據(jù)量,轉(zhuǎn)換到75M數(shù)據(jù)的時(shí)候,Excel就不給轉(zhuǎn)了,就彈出Excel中“value,range,GetIDsOfNames”函數(shù)出錯(cuò),Excel大小有限制嗎?我這個(gè)差不多就是做9×3文件循環(huán)數(shù)據(jù)插入,第10個(gè)循環(huán)就錯(cuò)了。我每次轉(zhuǎn)一個(gè)文件后都加了Sleep(2000),能幫忙解決一下嗎?  回復(fù)  更多評(píng)論


            本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/vicozo/archive/2009/04/12/4067804.aspx
            ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            _Application   ExcelApp;    
              Workbooks   wbsMyBooks;    
              _Workbook   wbMyBook;    
              Worksheets   wssMysheets;    
              _Worksheet   wsMysheet;    
              Range   rgMyRge;    
              //創(chuàng)建Excel   2000服務(wù)器(啟動(dòng)Excel)    
               
              if   (!ExcelApp.CreateDispatch("Excel.Application",NULL))    
              {    
              AfxMessageBox("創(chuàng)建Excel服務(wù)失敗!");    
              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,此時(shí),rgMyRge是cells的集合    
              rgMyRge.AttachDispatch(wsMysheet.GetCells(),true);    
              //設(shè)置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("時(shí)間"));  
              rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)3),_variant_t("信息"));  
              rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)4),_variant_t("級(jí)別"));  
              rgMyRge.SetItem(_variant_t((long)1),_variant_t((long)5),_variant_t("確認(rèn)時(shí)間"));  
              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);    
               
               
              //得到第一列   //設(shè)置列寬    
              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));    
               
              //釋放對(duì)象    
              rgMyRge.ReleaseDispatch();    
              wsMysheet.ReleaseDispatch();    
              wssMysheets.ReleaseDispatch();    
              wbMyBook.ReleaseDispatch();    
              wbsMyBooks.ReleaseDispatch();    
              ExcelApp.ReleaseDispatch();   

            本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處: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;  
              } 


            本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/vicozo/archive/2009/04/12/4067838.aspx

            posted @ 2011-02-15 08:58 wrh 閱讀(2460) | 評(píng)論 (0)編輯 收藏
            僅列出標(biāo)題
            共25頁(yè): 1 2 3 4 5 6 7 8 9 Last 

            導(dǎo)航

            <2013年1月>
            303112345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789

            統(tǒng)計(jì)

            • 隨筆 - 268
            • 文章 - 11
            • 評(píng)論 - 52
            • 引用 - 0

            常用鏈接

            留言簿(19)

            隨筆檔案

            文章檔案

            收藏夾

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久久久人妻一区二区三区| 久久精品中文字幕大胸| AV无码久久久久不卡蜜桃| 久久只这里是精品66| 久久久女人与动物群交毛片| 久久96国产精品久久久| 久久亚洲AV无码西西人体| 亚洲精品国精品久久99热| 久久人人爽爽爽人久久久| 国产日韩欧美久久| 欧美亚洲国产精品久久| 麻豆精品久久精品色综合| 欧美精品丝袜久久久中文字幕| 久久伊人五月丁香狠狠色| 久久精品国产秦先生| 中文国产成人精品久久亚洲精品AⅤ无码精品 | 欧美熟妇另类久久久久久不卡| 久久久91精品国产一区二区三区 | 成人久久综合网| 欧美成人免费观看久久| 久久综合九色综合久99 | 久久一区二区免费播放| 嫩草伊人久久精品少妇AV| 伊人久久大香线蕉综合网站| 国产免费福利体检区久久| 69久久精品无码一区二区| 精品综合久久久久久97| 狠狠色丁香婷婷综合久久来来去| 无码超乳爆乳中文字幕久久| 久久久精品久久久久影院| 久久激情亚洲精品无码?V| 好久久免费视频高清| 久久国产精品77777| 亚洲AV无一区二区三区久久| 久久免费视频1| 2021国内久久精品| 综合久久精品色| 久久99久久99精品免视看动漫| 国产精品久久久久免费a∨| 国产精品久久久久久五月尺| 中文字幕精品无码久久久久久3D日动漫|