宁静以致远
zgf的blog
<2009年7月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

留言簿(17)

随笔分类

随笔档案

文章档案

友情链接

资料收藏

搜索

最新评论

阅读排行榜

评论排行榜

 
VC知识库BLOG   首页  新随笔  联系  聚合  登录 
  随笔-33 文章-8 评论-117 Trackbacks-0
2009年3月12日
     前几天同事用sf上的一个网络类库写了个服务器。一测试发现性能很差。最多每秒才能处理500次请求,并且是在网络很好的情况下,隔两个交换机后客户端就只能收到200次/秒的正确响应了。同事忙着做其它事,改进服务器的任务就交给我了。
     项目中客户端的请求仅是有20bytes的数据,并且只有一小部分需要服务器回复500bytes左右的数据。综合考虑各种网络模型后我决得IOCP模型更适合当前的应用。
     IOCP模型的使用方法很多资料都有。《windows网络编程》(第二版)讲得很好。随书光盘中有使用IOCP模型的简单但很好的例子。我的服务器程序写完时还没看到这个示例,正被其它示例代码里的锁弄的晕头转向。当看到这个示例后发现那些锁都是多余的。剩下的代码基本上差不多。

     有点不同的是PER_IO_DATA的处理上。所有的例子在往完成端口投递请求时都先分配了一个PER_IO_DATA,请求处理后马上释放。考虑到我的应用中所有请求全部是短连接,并且数据量很小,我觉得分配和释放是浪费的。每个PER_IO_DATA对应一个socket,当socket关闭后这个PER_IO_DATA不必释放,而是用来准备接受下一个连接socket。
     下面是我写的工作线程代码:
DWORD WINAPI ServerWorkerThread(LPVOID lpparam)
{
    CIocpServer 
* pServer = (CIocpServer*)lpparam;
    DWORD BytesTransferred;
    SOCKET socket;
    LPPER_IO_OPERATION_DATA PerIoData;
    BOOL close_socket 
= FALSE;
    
int ret;
    
while(pServer->bRun)
    
{
        ret 
= GetQueuedCompletionStatus(pServer->CompletionPort, &BytesTransferred,
            (LPDWORD)
&socket, (LPOVERLAPPED *&PerIoData, INFINITE);
        
if (ret == ERROR_SUCCESS)
        
{
            DWORD last_error 
= GetLastError();
            
if(last_error == ERROR_SUCCESS)
                
return 0;                //完成端口被关闭,退出
            if(ERROR_NETNAME_DELETED == last_error   
                
|| ERROR_OPERATION_ABORTED == last_error)
                close_socket 
= TRUE;    //socket被关闭 或者 操作被取消
            else
                
continue;
        }


        
if (BytesTransferred == 0)
        
{
            
if(socket == 0 && PerIoData == 0)
                
break;

            closesocket(PerIoData
->socket);

            pServer
->Accept(PerIoData);

            
continue;
        }


        
if(PerIoData->eType == IO_EVENT_ACCEPT)
        
{
            setsockopt(PerIoData
->socket,SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&(pServer->m_server), sizeof(pServer->m_server) ) ;

            
if(CreateIoCompletionPort((HANDLE) PerIoData->socket, pServer->CompletionPort, (DWORD) PerIoData->socket,0== NULL)
                printf(
"CreateIoCompletionPort error:%d ", GetLastError());

            ret 
= pServer->Recv(PerIoData,BytesTransferred);
        }

        
else if(PerIoData->eType == IO_EVENT_WSARECV)
        
{
            ret 
= pServer->Recv(PerIoData,BytesTransferred);
        }

        
else if(PerIoData->eType == IO_EVENT_WSASEND)
        
{
            ret 
= pServer->Send(PerIoData,BytesTransferred);
        }


        
if(ret == FALSE)
        
{
            closesocket(PerIoData
->socket);
            pServer
->Accept(PerIoData);

        }

    }


    
return 0;
}


      可以看到,每个AccepteEx接入的客户端连接对应着一个PER_IO_DATA,并伴随着该连接的整个生命周期。当连接结束后,PER_IO_DATA马上又通过投递AccepteEx准备下一个连接。这样可以避免平凡的分配和释放内存。
发表于 2009-03-12 00:01 zgf的blog 阅读(1095) | 评论 (4)编辑 收藏
2009年3月11日

        服务器在1G内存,奔腾E2140(双核 1.6G),windows xp系统的机器上每秒能处理1万次左右的TCP短连接请求。网络流量为20M/s 。CPU占用率80-90% 。以前没专门做过服务器软件,也不知道性能怎么样。
        下面是客户端测试线程代码。每个客户端跑200个线程,共运行了两个测试客户端。

while(1)
{
if (!(sock.Connect("192.168.1.8",SYNC_PORT)))
{
    nMsgError++;
    MySleep(100);
    continue;
}

dwRet = sock.Send((char *)&syncmsg, sizeof(MsgUpdateSync_T));
if (dwRet < 0)
{
  MySleep(100);
  nDownloadError++;
  wprintf(L"Send Update Sync Msg Failed ");
  sock.Disconnect();
  continue;
}

else
{ 
  nOk++;
  sock.Disconnect();
  continue;
}

}

          实际运行时每个客户成功的请求大约5000次/秒,失败的大约100次/秒。   服务端内存消耗4M。
发表于 2009-03-11 22:39 zgf的blog 阅读(1372) | 评论 (4)编辑 收藏
2008年9月27日
vmware文件可以到http://www.namipan.com/downfile/cepc.rar/84f4653dda89d02ef820fa11da631450e29a0f639e468100下载。
里面有做好的wince6系统。支持网络和U盘。光驱的支持懒得去修改了。
bsp就不上传了。因为更本就是cepc。
就是说用cepc的bsp可以直接编译出vmware能跑的nk.bin。 但网络,U盘和光驱都都有问题。需要参考
如何制作支持VMWare的Windows CE BSP》文章做修改。

顺便上传一张浏览网页的截图:
发表于 2008-09-27 08:50 zgf的blog 阅读(829) | 评论 (0)编辑 收藏
2008年9月20日

    前几天还特意找wince下读写ini文件的函数,却意外的在ce的源代码中发现了。有需要的可以参考一下


//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//

#include "iniutil.h"


BOOL ReadIni(LPCWSTR pwszSection, LPCWSTR pwszKey, LPWSTR pwszValue, size_t cchValue, LPCWSTR pwszIniFile)
{
   // GetPrivateProfileString is unavailable on CE

   BOOL bSuccess = FALSE;
   FILE * fin;
   WCHAR wszLine[256];
   LPWSTR pwsz = NULL;

   if (!pwszIniFile ||
      !pwszSection ||
      !pwszKey ||
      !pwszValue ||
      !cchValue)
   {
      goto leave;
   }

   fin = _wfopen(pwszIniFile, L"r");
   if (fin)
   {
      while (fgetws(wszLine, lengthof(wszLine), fin))
      {
         if (!wcsstr(wszLine, pwszSection)) // Section
         {
            continue;
         }

         while (fgetws(wszLine, lengthof(wszLine), fin)) // Key & Value
         {
            pwsz = wcschr(wszLine, L'=');
            if (!pwsz)
            {
               continue;
            }

            *pwsz = L'\0'; // Erase the '='

            if (0 != _wcsicmp(wszLine, pwszKey)) // Key
            {
               continue;
            }

            pwsz++; // Point to the value

            bSuccess = SUCCEEDED(StringCchCopyEx(
                                    pwszValue,
                                    cchValue,
                                    pwsz,
                                    &pwsz,
                                    NULL,
                                    0
                                    ));

            if (bSuccess)
            {
               if (*--pwsz == L'\n')
               {
                  *pwsz = L'\0';
               }
            }
         }
      }

      fclose(fin);
   }

leave:
   return bSuccess;
}


BOOL WriteIni(LPCWSTR pwszSection, LPCWSTR pwszKey, LPWSTR pwszValue, size_t cchValue, LPCWSTR pwszIniFile)
{
   // WritePrivateProfileString is unavailable on CE

   BOOL bSuccess = FALSE;
   BOOL bReplace;
   FILE * fin;
   fpos_t posSrc;
   fpos_t posDest;
   size_t cchLine;
   size_t cchNewLine;
   WCHAR wszLine[256];
   WCHAR wszNewLine[256];
   LPWSTR pwsz = NULL;
   DWORD dwAttrib;

   if (!pwszIniFile ||
      !pwszSection ||
      !pwszKey ||
      (!pwszValue && cchValue) ||
      (cchValue >= lengthof(wszLine)))
   {
      goto leave;
   }

   dwAttrib = GetFileAttributes(pwszIniFile);
   if (dwAttrib == -1)
   {
      goto leave;
   }

   SetFileAttributes(pwszIniFile, dwAttrib & ~FILE_ATTRIBUTE_READONLY);

   fin = _wfopen(pwszIniFile, L"r+");
   if (fin)
   {
      bReplace = FALSE;
      cchNewLine = 0;

      while (fgetws(wszLine, lengthof(wszLine), fin))
      {
         if (!wcsstr(wszLine, pwszSection)) // Section
            continue;

         if (fgetpos(fin, &posSrc) || fgetpos(fin, &posDest))
         {
            goto closeFile;
         }

         while (fgetws(wszLine, lengthof(wszLine), fin)) // Key & Value
         {
            cchLine = wcslen(wszLine);

            pwsz = wcschr(wszLine, L'=');
            if (!pwsz)
            {
               goto nextLine;
            }

            *pwsz = L'\0'; // Erase the '='

            if (0 != _wcsicmp(wszLine, pwszKey)) // Key
            {
               *pwsz = L'='; // Add the '=' back
               goto nextLine;
            }

            *pwsz = L'='; // Add the '=' back
            pwsz++; // Point to the value

            // Clear Key & Value on NULL or empty string
            if (!pwszValue || !cchValue)
            {
               bReplace = TRUE;
               cchLine = 0;
               goto nextLine;
            }

            // Do not overwritte more than the number of read characters.
            size_t cchKey = (pwsz - wszLine);
            if (SUCCEEDED(StringCchCopyNEx(
                                    wszNewLine,
                                    lengthof(wszNewLine),
                                    wszLine,
                                    cchKey,
                                    &pwsz,
                                    &cchNewLine,
                                    0
                                    )) &&
               SUCCEEDED(StringCchCopyNEx(
                                    pwsz,
                                    cchNewLine,
                                    pwszValue,
                                    min((cchLine - cchKey), cchValue),
                                    &pwsz,
                                    &cchNewLine,
                                    STRSAFE_IGNORE_NULLS
                                    )))
            {
               bReplace = TRUE;

               BOOL bAppendNewLine = FALSE;
               if (wszLine[cchLine-1] == L'\n')
               {
                  cchLine--;
                  bAppendNewLine = TRUE;
               }

               // See if there are remaining characters to copy
               if (cchValue > (cchLine - cchKey))
               {
                  if (bAppendNewLine)
                  {
                     cchLine++;
                  }

                  pwszValue += (cchLine - cchKey);
                  if (FAILED(StringCchCopyNEx(
                                       wszLine,
                                       lengthof(wszLine),
                                       pwszValue,
                                       (cchValue - (cchLine - cchKey)),
                                       &pwsz,
                                       &cchLine,
                                       0
                                       )) ||
                     (bAppendNewLine &&
                     FAILED(StringCchCopy(pwsz, cchLine, L"\n"))) ||
                     FAILED(StringCchLength(wszLine, lengthof(wszLine), &cchLine)))
                  {
                     bReplace = FALSE;
                  }
               }
               else
               {
                  if (bAppendNewLine &&
                     FAILED(StringCchCopy(pwsz, cchNewLine, L"\n")))
                  {
                    bReplace = FALSE;
                  }
                  cchLine = 0;
               }

               if (FAILED(StringCchLength(wszNewLine, lengthof(wszNewLine), &cchNewLine)))
               {
                  bReplace = FALSE;
               }
            }

nextLine:
            if (bReplace)
            {
               BOOL bEOF = (BOOL)feof(fin);

               // Save read offset, then restore write offset
               if (fgetpos(fin, &posSrc) || fsetpos(fin, &posDest))
               {
                  goto closeFile;
               }

               if (cchNewLine)
               {
                  bSuccess = (WEOF != fputws(wszNewLine, fin));
                  if (cchLine &&
                     SUCCEEDED(StringCchCopyN(
                                          wszNewLine,
                                          lengthof(wszLine),
                                          wszLine,
                                          cchLine
                                          )))
                  {
                     cchNewLine = cchLine;
                  }
                  else
                  {
                     cchNewLine = 0;
                  }
               }
               else if (cchLine)
               {
                  bSuccess = (WEOF != fputws(wszLine, fin));
                  cchLine = 0;
               }

               // Save write offset, then restore read offset
               if (fgetpos(fin, &posDest) ||
                  bEOF ||
                  fsetpos(fin, &posSrc))
               {
                  goto closeFile;
               }
            }
            else
            {
               // Just update read and write offsets
               if (fgetpos(fin, &posSrc) || fgetpos(fin, &posDest))
               {
                  goto closeFile;
               }
            }
         }
      }

closeFile:
      if (bReplace && !fsetpos(fin, &posDest))
      {
         if (cchNewLine)
         {
            bSuccess = (WEOF != fputws(wszNewLine, fin));
         }
         SetEndOfFile(_fileno(fin));
      }

      fclose(fin);
   }

   SetFileAttributes(pwszIniFile, dwAttrib);

leave:
   return bSuccess;
}

发表于 2008-09-20 14:41 zgf的blog 阅读(2555) | 评论 (8)编辑 收藏
2008年8月26日
        网上有wince5版本的cepc。我参考他的过程做了个ce6.0版本的。
发表于 2008-08-26 07:06 zgf的blog 阅读(2424) | 评论 (6)编辑 收藏
2008年7月14日

        本来不想用ttf字体的。因为变态的硬件工程师只接了4M Bytes的nor flash。除去系统和驱动,给应用程序剩下的空间只有2M。这年头2M的flash够干嘛呀!  但新的客户要求程序更花哨,更漂亮,添加了很多效果。其中不同大小的字就有几种。如果用点阵字库,一种字体也需要创建多种字库。而客户给的汉仪菱心体ttf字库才1.5M bytes。综合比较觉得使用ttf字体在存储空间上会划算一些。 参照http://www.minigui.org/cgi-bin/lb5000/topic.cgi?forum=6&topic=5548帖子的步骤终于添加了ttf的支持。步骤如下

1 . 安装freetype-1.3.1库
      手动编译成动态库后 编译minigui程序总是报错误error: no memory region specified for loadable section `.plt'  。 我只好编译成静态库libttf.a。将库拷贝到编译器的连接库目录。在编译器的inlcude目录下创建freetype1目录。并将头文件拷贝到该目录下。

2. 启用ttf
     可以按常规make menuconfig中启用。我直接修改config.h文件,找到#undef  _TTF_SUPPORT, 改为#define _TTF_SUPPORT 1 ,找到#undef  _UNICODE_SUPPORT, 改为#define _UNICODE_SUPPORT 1 。 然后make install编译minigui库

3. MiniGUI.cfg文件修改
     将arial.ttf和stxinwei.ttf 拷贝到设备的/uar/local/lib/minigui/res/fonts目录下。
    truetypefonts改为
[truetypefonts]
font_number=2
name0=ttf-arial-rrncnn-0-0-ISO8859-1
fontfile0=/usr/local/lib/minigui/res/font/arial.ttf
name1=ttf-stxinwei-rrncnn-0-0-GB2312
fontfile1=/usr/local/lib/minigui/res/font/stxinwei.ttf

4. 应用程序编写

    HWND hwnd;
    HDC hdc;
    HWND timeedit, spin;
    SIZE size;
    
    /* 创建编辑框使用的逻辑字体 */
    timefont = CreateLogFont ("ttf", "stxinwei", "GB2312",
    FONT_WEIGHT_REGULAR, FONT_SLANT_ROMAN, FONT_SETWIDTH_NORMAL,
    FONT_SPACING_CHARCELL, FONT_UNDERLINE_NONE, FONT_STRUCKOUT_NONE,
    30, 0);

    spin = CreateWindow (CTRL_SLEDIT,
    "nihao?",
    WS_VISIBLE,
    10241,
    0, 250, 400, 200, hWnd, 0);  

    SetWindowFont (spin, timefont); 


    注意CreateLogFont 创建的字体大小好像不能超过32。超过后显示汉字会异常。还没有查原因。英文则可以更大。另外不知是不是库的问题,我的应用程序在不使用ttf库前,9M的空闲内存可以跑好几次。而使用ttf库后,仅仅连接了ttf库,跑一次都经常内存不够。也不知道什么原因。




发表于 2008-07-14 21:24 zgf的blog 阅读(1890) | 评论 (2)编辑 收藏
 

        同事在wince下写了个网络驱动,让我帮忙写个小程序测试网络流量。这还不简单。三下五除二一下子写了个测试程序出来。主体思想是建立连接后创建两个线程,一个专门收,一个专门发。然后定时统计收发的数据长度。主要代码如下:

//发送线程
void * __stdcall thread_send(void * lpparam)
{
 int slen;
 SOCKET m_sock = (SOCKET)lpparam;
 char * send_buf = (char *)malloc(1024);

 while(!bthreadstop)
 {
  slen = send(m_sock,send_buf,1024,0);
  if(slen == SOCKET_ERROR)
  {
   CString str;
   str.Format(_T("send error:%d"), WSAGetLastError());
   AfxMessageBox(str);
   break;
  }
  total_send += slen;
  Sleep(50);
 }

 free(send_buf);

 return 0;
}
//接收线程
void * __stdcall thread_recv(void * lpparam)
{
 int rlen;
 SOCKET m_sock = (SOCKET)lpparam;
 char * recv_buf = (char *)malloc(1024);

 while(!bthreadstop)
 {
  rlen = recv(m_sock,recv_buf,1024,0);
  if(rlen == SOCKET_ERROR)
  {
   CString str;
   str.Format(_T("recv error:%d"), WSAGetLastError());
   AfxMessageBox(str);
   break;
  }
  total_recv += rlen;
 }

 free(recv_buf);

 return 0;
}

void CTcptest_ceDlg::OnButtonConnect()
{
    ......        //连接代码省略
   
   //connect ok.
    CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread_recv,(void *)m_sock,0,0);
    CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)thread_send,(void *)m_sock,0,0);
}

     满怀信心的交给同事去测试。才一会儿问题就出现了。使用同样的代码,一个用evc编译后在wince模拟器上跑,另一个用vc编译后在PC上跑。
现象:
1.  终端发  PC收  持续一分钟没断线。
2.  终端收 PC发 持续一分钟没断线。
3. 终端和PC同时收发,几秒后终端发送和接收的数据都为零。

         改用两台PC互相通信。 同样的代码,同时收发速度达到11.9Mbyte/s。这个速度还算正常,也证明了我的代码至少表面上是OK的。

         添加打印后发现 PC 在开始的1秒内发送64K的数据后就停住了,后面持续很久发送数据量都为零。总的表现就是PC端猛的发一下数据,然后等待,然后又猛的发一下数据,又等待。如是循环。 根据http://www.techrss.cn/html/2007/05-20/18912.htm帖子说明似乎是PC发送太快了。 在PC的send线程中sleep一下,情况确实有所改善。等待10ms时,传输大约3M的数据后不能再发送。 等待50ms时,传输了大约50M的数据。

         这是个很奇怪的问题。为什么PC发得太快会导致wince终端网络不能收发呢?

           

        

发表于 2008-07-14 20:31 zgf的blog 阅读(2299) | 评论 (10)编辑 收藏
2008年4月28日
          一般嵌入式产品量产时烧写bin文件包含了整个内核和文件系统,这样所有的产品的MAC地址就是一样的。批量自动更改MAC地址呢?自己做个烧写器是个办法,这样可以用程序自动修改烧写的bin文件,不过不可行。
发表于 2008-04-28 09:56 zgf的blog 阅读(2799) | 评论 (8)编辑 收藏
2008年4月27日
            分析AEC程序时认为运算量重点在滤波和更新滤波器系数部分。这两个部分也最早将浮点运算转换成了定点运算。而判断远端是否讲话部分程序很简单,只有三个浮点乘法,就没有换成定点运算。在花了一番功夫将滤波部分用汇编实现后才发现优化的时间占总比重很小,而时间大部分花在三个浮点乘法上。
            将代码一部分一部分的注释掉后统计时间差异。结果:  (总共43.88秒的语音数据)
                              有三个浮点的代码          全定点的c代码                滤波部分用汇编实现的代码
时间(毫秒)                 8010                                     2220                                   1772
             
            处理器是bf536  400MHz 。也就是说定点c代码只用了20MHz的处理器资源,速度足够了。
            虽然走了弯路,但用汇编实现关键代码也是有益的,仅滤波部分就省了25%的时间。
            bf536有两个乘法器,理论上可以加快速度一倍。实际上由于数据对齐问题只有一半的数据能使用汇编。下面是滤波部分的代码
//c代码
static int filt16_c(short *coe, short * buf, int tap)
{
 int j;
 int tmp = 0;
 for(j=0;j<tap;j++)  
    tmp += buf[j]*coe[j];
 return tmp;
}
//汇编代码
.global _filt16_asm;
.type _filt16_asm,STT_FUNC;
_filt16_asm :
 I0 = R0;
 P1 = R1;
 A1 = A0 = 0;
 R2 >>= 1;
 P0 = R2;
LSETUP(loop1, loop1) LC0 = P0; 
 R1 = [ P1++ ] || R0 = [ I0++ ];
loop1: A0 += R0.L * R1.L , A1 += R0.H * R1.H(IS) || R1 = [ P1++ ] || R0 = [ I0++ ];
 R0 = A0 , R1 = A1;
 R0 = R0 + R1;
 RTS;
.size _filt16_asm, .-_filt16_asm

发表于 2008-04-27 17:39 zgf的blog 阅读(1354) | 评论 (2)编辑 收藏
2008年4月25日

       minigui网站上有开源的eDillo浏览器下载。但需要1.6.8版本的minigui支持。而1.6版本的开源版很简单,不能实用。于是看能不能用1.33版的minigui库编译eDillo。
       编译时指定1.33库,发现只少了DrawTextEx2函数,参考1.6版的开源代码在1.33版中实现DrawTextEx2函数。很快编译通过。
       直接运行simple-edillo会显示程序中的默认页面。但导航到google主页会异常。不知道是什么原因。


参考DrawTextEx添加到minigui库中。代码可以跑,肯定有问题,但至少可以显示eDillo中的默认网页

int DrawTextEx2(HDC hdc, const char* pText, int nCount,
                RECT* pRect, int indent, UINT nFormat,DTFIRSTLINE *firstline)
{
    PDC pdc;
    int n, nLines = 0, width = 0;
    BOOL bOutput = TRUE;
    int x, y;
    RECT rcDraw, rcOutput;
    int nTabWidth, tabs;
    SIZE size;
    int line_height;

 firstline = NULL;

    pdc = dc_HDC2PDC(hdc);

    if (nCount == -1)
        nCount = strlen (pText);

    line_height = pdc->pLogFont->size + pdc->alExtra + pdc->blExtra;

    if (nFormat & DT_TABSTOP)
        nTabWidth = HIWORD (nFormat) *
                    (*pdc->pLogFont->sbc_devfont->font_ops->get_ave_width)
                    (pdc->pLogFont, pdc->pLogFont->sbc_devfont);

    else
        nTabWidth = pdc->tabstop *
                    (*pdc->pLogFont->sbc_devfont->font_ops->get_ave_width)
                    (pdc->pLogFont, pdc->pLogFont->sbc_devfont);

    // Transfer logical to device to screen here.
    rcDraw = *pRect;
    coor_LP2SP(pdc, &rcDraw.left, &rcDraw.top);
    coor_LP2SP(pdc, &rcDraw.right, &rcDraw.bottom);
    NormalizeRect (&rcDraw);

    if (dc_IsGeneralDC (pdc)) {
        LOCK (&pdc->pGCRInfo->lock);
        if (!dc_GenerateECRgn (pdc, FALSE))
            bOutput = FALSE;
    }

#ifdef _LITE_VERSION
    if (CHECK_DRAWING (pdc)) bOutput = FALSE;
#endif

    // Draw text here.
    if (nFormat & DT_CALCRECT || firstline)
        bOutput = FALSE;

    y = rcDraw.top;
    if (nFormat & DT_SINGLELINE) {
        if (nFormat & DT_BOTTOM)
            y = rcDraw.bottom - pdc->pLogFont->size;
        else if (nFormat & DT_VCENTER)
            y = rcDraw.top + ((RECTH (rcDraw) - pdc->pLogFont->size) >> 1);
    }

    while (nCount != 0) {
        int nOutput;
        int maxwidth;

        if (nLines == 0) {
            maxwidth = rcDraw.right - rcDraw.left - indent;
            if (maxwidth <= 0) {
                // new line
                y += pdc->pLogFont->size;
                nLines ++;
                continue;
            }
        }
        else
            maxwidth = rcDraw.right - rcDraw.left;
       
        gdi_start_new_line (pdc->pLogFont);
        tabs = txtGetOneLine (pdc, pText, nCount, nTabWidth, maxwidth, nFormat, &n);

        gdi_get_TextOut_extent (pdc, pdc->pLogFont, pText, n, &size);
        width = size.cx + tabs * nTabWidth;
        n += tabs;

        if ( (pText[n-1] == '\n' || pText[n-1] == '\r')
             && !(nFormat & DT_SINGLELINE) ) {
            int tmpx = 0, tmpy = 0;
            nOutput = n - 1;
            width -= gdi_width_one_char (pdc->pLogFont, pdc->pLogFont->sbc_devfont,
                            pText + n - 1, 1, &tmpx, &tmpy);
        }
        else
            nOutput = n;
           
        if (nFormat & DT_RIGHT)
            x = rcDraw.right - width;
        else if (nFormat & DT_CENTER)
            x = rcDraw.left + ((RECTW (rcDraw) - width) >> 1);
        else
            x = rcDraw.left;
        x += (nLines ? 0 : indent);

        if (firstline) {
            firstline->nr_chars = nOutput;
            firstline->startx = x;
            firstline->starty = y;
            firstline->width = width;
            firstline->height = line_height;
            break;
        }  

        rcOutput.left   = x;
        rcOutput.top    = y;
        rcOutput.right  = rcOutput.left + width;
        rcOutput.bottom = rcOutput.top + line_height;

        if (nFormat & DT_CALCRECT) {
            if (nLines == 0)
                *pRect = rcOutput;
            else
                GetBoundRect (pRect, pRect, &rcOutput);
        }

        // draw one line
        if (bOutput && width > 0) {
            if (nFormat & DT_NOCLIP)
                txtDrawOneLine (pdc, pText, nOutput, x, y,
                        &rcOutput, nFormat, nTabWidth);
            else {
                RECT rcClip;
                IntersectRect (&rcClip, &rcOutput, &rcDraw);
                txtDrawOneLine (pdc, pText, nOutput, x, y,
                        &rcClip, nFormat, nTabWidth);
            }
        }

        pText += n;

        // new line
        y += line_height;
        nLines ++;

        // left characters
        nCount = nCount - n;
    }

    /* we are done, so release global clipping region */
    UNLOCK_GCRINFO (pdc);

    if (firstline) {
        coor_SP2LP (pdc, &firstline->startx, &firstline->starty);
        return 0;
    }

    if (nFormat & DT_CALCRECT) {
        coor_SP2LP (pdc, &pRect->left, &pRect->top);
        coor_SP2LP (pdc, &pRect->right, &pRect->bottom);
    }

    if (!(nFormat & DT_CALCRECT)) {
        // update text out position
        x += width;
        y -= line_height;
        coor_SP2LP (pdc, &x, &y);
        pdc->CurTextPos.x = x;
        pdc->CurTextPos.y = y;
    }

    return line_height * nLines;
}

发表于 2008-04-25 11:18 zgf的blog 阅读(1495) | 评论 (2)编辑 收藏