《VC++深入详解–学习笔记》(5)文本编程

Filed Under (VC++ 学习笔记) by panmaoru on 09-12-2009

本章简单介绍了一些关于文本编程的知识,其中比较重要的几个概念:插入符,窗口重绘,字符输入,定时器。

5.1 插入符

插入符Caret用来提示用户当前相应的位置。如果想要在程序中创建插入符,可以用CWnd类的CreateSolidCaret函数来完成,函数原型如下。

Void CreateSolidCaret(int nWidth,int nHeight);//两个参数分别表示插入符的宽度和高度

在一个MFC的单文档应用程序中,对文本的操作通常在View类中实现,要在View窗口创建之后再创建一个插入符,需要使用OnCreate函数。插入符的宽度通常为当前设备表述表中字符的平均宽度的1/8,高度和字符高度相同。

int CTextView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
       if (CView::OnCreate(lpCreateStruct) == -1)
              return -1;
       // TODO: Add your specialized creation code here
       CClientDC dc(this);
       TEXTMETRIC tm;
       dc.GetTextMetrics(&tm);
       CreateSolidCaret(tm.tmAveCharWidth/8,tm.tmHeight);
       ShowCaret();
       return 0;
}

5.2窗口重绘

View类窗口产生和重绘时都要调用OnDraw函数。

5.3字符输入

1.消息捕获

字符输入属于WM_CHAR消息,可以通过捕获WM_CHAR消息来获取键盘输入信息。

2.应该在插入符的位置输出键盘输入信息,输入之前需要先设定插入符位置,输出的时候相应修改插入符位置,以便下次输入输出,在View类区域的任何位置点击鼠标,设定插入符位置。

void CTextView::OnLButtonDown(UINT nFlags, CPoint point)
{
       // TODO: Add your message handler code here and/or call default
       m_ptOrigin=point;
       SetCaretPos(point);
       //SetCaretPos(m_ptSecond);一个类中同时只能有一个插入符
       m_strLine.Empty();
       CView::OnLButtonDown(nFlags, point);
}

3.回车与退行,字体

void CTextView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
       // TODO: Add your message handler code here and/or call default
       CClientDC dc(this);       //设置字体
       CFont font;
       font.CreatePointFont(200,"华文楷体",NULL);
       CFont *pOldFont=dc.SelectObject(&font);
       TEXTMETRIC tm;
       dc.GetTextMetrics(&tm);
       if(0x0d==nChar)//回车
       {
              m_strLine.Empty();
              m_ptOrigin.y+=tm.tmHeight;
       }
       else if(0x08==nChar)//退行
       {
              COLORREF clr=dc.SetTextColor(dc.GetBkColor());
              dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);
              m_strLine=m_strLine.Left(m_strLine.GetLength()-1);
              dc.SetTextColor(clr);
       }
       else
       {
              m_strLine+=nChar;
       }
       //输出文本之前先重定位插入符的位置
       CSize cs=dc.GetTextExtent(m_strLine);
       CPoint pt;
       pt.x=m_ptOrigin.x+cs.cx;
       pt.y=m_ptOrigin.y;
       SetCaretPos(pt);
       dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);
       dc.SelectObject(pOldFont);
       CView::OnChar(nChar, nRepCnt, nFlags);
}

5.4定时器Timer

CWnd类的SetTimer成员函数可以设置定时器,该函数的声明形式如下表示:

UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer) (HWND, UINT, UINT, DWORD) );

添加一个WM_Timer函数

void CTextView::OnTimer(UINT nIDEvent)
{
       // TODO: Add your message handler code here and/or call default
       MessageBox("。。。");
       CView::OnTimer(nIDEvent);
}

并在View类的OnCreate函数内设置一个定时器SetTimer

SetTimer(1,1,NULL);

运行,开启声音,彷如机关枪扫射的感觉,没几秒钟,程序就自动卡死了。

《VC++深入详解–学习笔记》(4)简单绘图

Filed Under (VC++ 学习笔记) by panmaoru on 09-12-2009

本章介绍了MFC的消息映射机制,探讨发送给窗口的消息是如何被MFC框架通过窗口句柄映射表和消息映射表来用窗口类的处理函数进行响应的。讨论设备描述表及其封装类CDC的应用,结合画图程序进行分析。

4.1消息映射机制

为视类添加一个鼠标左键按下的消息,在源文件中会添加3处代码。

1.[DrawView.h]

protected:
       //{{AFX_MSG(CDrawView)
       afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
       //}}AFX_MSG
       DECLARE_MESSAGE_MAP()

此次为一个OnLButtonDown函数的声明。

2. [DrawView.cpp]

BEGIN_MESSAGE_MAP(CDrawView, CView)
       //{{AFX_MSG_MAP(CDrawView)
       ON_WM_LBUTTONDOWN()
       //}}AFX_MSG_MAP
END_MESSAGE_MAP()//消息映射表

此处为将一个鼠标左键按下的消息和一个消息响应函数关联起来。

3. [DrawView.cpp]

void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
       // TODO: Add your message handler code here and/or call default
       //MessageBox("OK");
       CView::OnLButtonDown(nFlags, point);
}

OnLButtonDown函数实现。

MFC消息映射机制的具体实现方法是:在每个能接收和处理消息的类中(View,Frame。。)定义了一个消息和消息静态函数对照表,即消息映射表,在消息映射表中,消息与对应的消息处理函数指针是成对出现的。某个类能处理所有消息及其对应的消息处理函数的地址都列在这个对应的静态表中,当有消息需要处理时,程序只需要搜索改消息静态表,查看表中是否有改信息,就知道该类能否处理该消息,如果能,则通过静态表找到与之对应的消息处理函数。

在CDrawView类中,CDrawView对象相关一个窗口,当然有它的窗口句柄,该句柄与CDrawView对象的一个指针CDrawView*存在一一对应的关系。

4.2绘制线条

步骤1:定义一个CPoint全局变量,存放鼠标按下去的时候的起点信息;

步骤2:在鼠标按下去消息处记录鼠标位置;

步骤3:记录鼠标弹起时的位置,并在2个位置之间划一条线。

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
       // TODO: Add your message handler code here and/or call default
       HDC hdc;
       hdc=::GetDC(m_hWnd);
       MoveToEx(hdc,m_point.x,m_point.y,NULL);
       LineTo(hdc,point.x,point.y);
       ::ReleaseDC(m_hWnd,hdc);
       CView::OnLButtonUp(nFlags, point);
}

4.2.2MFC的CDC绘制线条

       CDC* pDC=GetDC();
       pDC->MoveTo(m_point);
       pDC->LineTo(point);
       ReleaseDC(pDC);

4.2.3MFC的CClientDC类绘制线条

       CClientDC dc(this/*GetParent()//在父框架上绘图*/);
       //CPen pen(PS_SOLID,1,RGB(255,0,0)); 新建一个画笔对象
       // CPen * oldPen=dc.SelectObject(&pen); 将画笔对象选人设备描述表,并将原先的保存
       dc.MoveTo(m_point.x,m_point.y);
       dc.LineTo(point.x,point.y);
       //dc.SelectObject(oldPen); //将原先的画笔对象选择回

4.2.4MFC的CWindowDC类绘制线条

       CWindowDC dc(GetDesktopWindow());
       dc.MoveTo(m_point.x,m_point.y);
       dc.LineTo(point.x,point.y);

4.3静态数据成员与函数

静态数据成员和函数属于类的本身,在类加载的时候为其分配空间。非静态成员函数和变量属于对象的方法和数据。静态成员函数只能访问静态数据成员。

《VC++深入详解–学习笔记》(3)MFC框架程序剖析

Filed Under (VC++ 学习笔记) by panmaoru on 08-12-2009

第三章讲的是MFC框架程序剖析,结合着视频和书看了一遍之后,只能有一个粗略的了解。先记下一个概念:MFC(Microsoft Function Class ,微软基础类库)。

MFC AppWizard

一个辅助生成源代码的向导工具,可以帮助我们自动生成基于MFC的源代码。第一章提到了创建一个Win32应用程序需要经过下列步骤。

  • 编写WinMain函数
  • 设计窗口类(WNDCLASS);
  • 注册窗口类(RegisterClass);
  • 创建窗口(CreateWindow);
  • 显示并更新窗口(ShowWindow|UpdateWindow);
  • 编写消息循环;
  • 编写窗口过程函数。

而基于MFC AppWizard的应用程序的在生成上和Win32相似,只是对其中的一些过程进行了封装,并提供了一些自定义。

全局变量和对象

Main函数是程序的入口函数,而在程序入口main函数加载之前,系统就会为全局变量和对象分配地址空间。

窗口类,窗口类对象和窗口

窗口是屏幕上的一块儿矩形区域;窗口类是封装了对窗口的一系列操作的类,比如注册窗口,创建窗口,显示窗口,销毁窗口等等;窗口对象是窗口类的一个实例。

C++窗口类对象和窗口并不是一回事,他们之间唯一的关系是C++窗口类对象内部定义了一个窗口句柄变量,保存了与这个C++窗口类对象相关的那个窗口的句柄,窗口销毁时,与之对象的C++窗口的类对象是否销毁要看其生命周期是否结束。但是如果窗口类对象销毁,那与之对应的窗口也将销毁。

动态创建一个按钮

在MFC提供的资源类中,有些类的构造直接通过其构造函数就可以完成,也就是说,这些对象的构造函数包含这个对象的初始化操作。但是有些对象的产生出了调用构造函数之外,还需要一些函数来进行初始化的工作。

  • 在MainFrame头文件中添加一个CButton对象
    private: CButton m_btn;
  • 调用Create函数,创建窗口。
    //BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
    m_btn.Create("Test",WS_CHILD|BS_DEFPUSHBUTTON,CRect(0,0,200,200),this,1);
    m_btn.ShowWindow(SW_SHOWNORMAL);

《VC++深入详解–学习笔记》(2)掌握C++

Filed Under (VC++ 学习笔记) by panmaoru on 01-12-2009

题外话

打开掌握C++这一章节,首先想到的是以前我非常喜欢的一个英语教师老罗说的一句话:一个人如果连自己的母语都学不好,那他很难学好一门外语。接触程序开发差不多有4,5年的时间了,现在看来,我是那个没有学好母语的人。

大学期间学的第一门程序设计语言是C++,成果可以用惨烈来形容,课程结束之后留下的印象大概就是:指针太繁琐了,C++博大精深啊!再后来便喜欢上了一种叫做asp.net的开发技术,因为它其中使用的C#语言是没有指针的。很不幸的是做asp.net开发这几年我只是粗略的翻过《C#入门经典-第三版》和《C#高级编程-第四版》,至于程序员必备的MSDN,我基本上都没安装过,罢了,从零开始,学习C++。

看《VC++深入详解》这本书之前,每天大概2个小时,花了半个月的把《C++ Primer-第四版》看了一遍,因为实际动手较少,只能说有一个初略的了解,对C就完全没有概念了,只能猜测下C和C++在基本的表达式,语句和控制流程等基础语法方面是类同的。

面向对象

C++是面向对象的程序设计语言,带类的C,比C多出了面向对象的特性,“面向对象”这个概念够大了,面向对象的主要特点有

  •  封装性:把数据和操作数据的函数组织在一起,不仅使程序更加紧凑,而且提高了类的内部数据的安全性;
  • 继承性:是一个类具有另外一个类的属性(数据)和行为(函数),增加了程序的可拓展性和代码的复用;
  • 多态性:一个接口,不同的实现(很难理解,很难解释)。

结构和类

写一个简单的C++程序:

//看完书本中的第一张,VC中很多结构的定义,烦

#include <iostream.h>

struct point//换成class

{

       int x;

       int y;

};

void main()

{

       point pt;

       pt.x=10;

       pt.y=12;

       cout<<"x="<<pt.x<<"y="<<pt.y<<endl;

}

程序运行,结果是正常的,输出赋值的x和y的值。

如果把struct换成class,用类来定义就会有问题,这里是由于访问权限的原因struct的数据成员默认是public的,class默认是private。刚开始不明白,后来想想,大概是类的封装特性吧,为了保护类的数据和类的成员函数。

《VC++深入详解–学习笔记》(1)Windows 内部运行机制

Filed Under (VC++ 学习笔记) by panmaoru on 30-11-2009

Windos系统提供了各种各样的函数,这些函数是windows操作系统提供给应用程序的接口 Application Progress Interface,所有主要的windows函数都在Windows.h头文件中进行了声明。

Win32 SDK(Soft Development Kit)是Windows 32平台下的软件开发包,包括API函数,帮助文档和一些辅助开发工具。

Windows是基于消息机制的,消息分标准消息,命令消息和通告消息。Windows程序中的消息又分进队消息和不进队消息,两种消息都由消息响应函数进行处理。

在VC中创建一个win32应用程序的大概流程如下:

  • 编写WinMain函数
  • 设计窗口类(WNDCLASS);
  • 注册窗口类(RegisterClass);
  • 创建窗口(CreateWindow);
  • 显示并更新窗口(ShowWindow|UpdateWindow);
  • 编写消息循环;
  • 编写窗口过程函数。

一个WinMain函数的原型如下:

int WINAPI WinMain(
  HINSTANCE hInstance,      // handle to current instance
  HINSTANCE hPrevInstance,  // handle to previous instance
  LPSTR lpCmdLine,          // command line
 int nCmdShow              // show state
);

此处WINAPI是一个_stdcall宏定义,VC有很多宏定义,这个很让人头大,需要慢慢去习惯,从网络上找了2中函数定义的说明与区别解释。

  1. __cdecl:这是编译器默认的函数调用转换方式,它可以处理可变参数的函数调用。参数的入栈顺序是从右向左。在函数运行结束后,由调用函数负责清理入栈的参数。在编译时,在每个函数前面加上下划线(_),没有函数名大小写的转换。即_functionname。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。
  2. __stdcall:函数参数从右向左入栈,被调用函数负责入栈参数的清理工作。函数名转换格式如下:_functionname@number。函数参数个数固定。

WINDOWS 的函数调用时需要用到栈( STACK ,一种先入后出的存储结构)。当函数调用完成后,栈需要清除,这里就是问题的关键,如何清除?

如果函数使用 _cdecl ,那么栈的清除工作是由调用者,用 COM 的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。

如果使用 __stdcall ,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用 __stdcall (虽然有时是以 WINAPI 的样子出现)。

那么为什么还需要 _cdecl 呢?当我们遇到这样的函数如 fprintf() 它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用 _cdecl 。

到这里有一个结论,如果你的程序中没有涉及可变参数,最好使用 __stdcall 关键字,我们创建一个Win32应用程序都是用的系统API函数,参数固定,所以都用__stdcall了。

沉思录

Filed Under (随网心情) by panmaoru on 27-11-2009

一个月了,感恩节附近,又是月底,本应该做月终小结之类的报告,思考了一下,并没有什么可说的,关键是兴致提不起来,干脆就零零散散写下来吧,感觉到疲倦了就点发布键。

前段时间看到ZZF的QQ签名写着”三年了,一事无成“,心里愕然一震,他是在说我。

这段时间常把过去的事情一件件翻出来,然后在脑海中虚构2个自己,一个指着另外一个说:这件事情,你做错了,可常又不免在转瞬之间做错另外一件事情。

话题有些沉重,说说工作的事情。

————————————————————————————————

公司做的是加拿大的(asp.net)网站外包,和国内的零零散散的做网站的团队没有太大区别。

一个月过去了,四个人的团队完成了公司的网站(无后台功能版),心态上的变化应该是这样的”惊讶>崩溃>习惯“,这份工作也坚定了我一个信念:这是我最后一份asp.net工作。

四周的时间,基本上都是在重复着已经厌倦的流程:做页面,写样式表。如果说收获,可能工作前两天研究了一些以前一直觉得先进的三层开发模式吧,且乘着热度完成了公司网站基础的实体类,数据逻辑层,业务逻辑层代码。今天得到消息,老板建议基于开源的cms基础作修改开发,便于产品化。也好,就这样吧,反正我已全无兴趣。

大四上学期的时候一个同学常在我面前炫耀他css写的有多好,学的多优秀,这件事刺激了我,于是寒假在家期间,一个人楞生生的把 sohu,sina,163三大门户网站的首页用css+xhtml写了一遍,然后心里暗暗叫嚣,这算什么嘛!之后,他便很少和我讨论css结构等等了。大学毕业找工作的时候,那三个所谓web标准化架构的作品让我得到了163,qq,ctrip等不错的网络公司的面试机会,我常哭笑不得大学毕业之后维持我谋生的不是科班的c++,数据结构,数据库,操作系统,组合数学等专业知识技能,而是无心插柳般学会的一门算不上语言或我一直不知道怎么该给它归类的东西。

大二的时候认识了学校做asp.net的几个学长,然后傻傻的认为这是这个世界上最有前途的开发技术,于是乎,从幼儿园到教务处,一个网站接一个网站的做着,熟练到是很熟练,却很少去思考更深入的东西。从05年初开始第一次动手做网站,到09年的现在,很少思考,或思考的很肤浅,知其然而不知其所以然。

04年初,大一下学期,学院开了c++课程,逃课,玩游戏是那段大学时光的主旋律,上机实验课的时候也多是玩游戏,看毛片消磨掉的。我常为此事纠结,难以释怀,工作的时候,我也常因为此事很不自信,心虚。虚度那段时光之后,我逃避了很多与之相关的事情,结果,大学四年我没学会一门真正的程序设计语言(html除外)。当自信的说我理解了面向对象的思想概念,已是毕业2年之后,就像我很喜欢的那部叫《肖申克的救赎》的电影中所阐述的那样吧,那个懵懂的孩子的过错已经弥补了。

偶尔会问自己:你现在最需要的是什么?答:一个肯定自己机会。这个机会也只有我自己能给。

牢记:付出,努力,执着,走过眼前的荆棘小路,牛B的大道就在前方。

心态与状态

Filed Under (随网心情) by panmaoru on 19-11-2009

心态

  • 茫茫人海,一眼望去,我是最穷的一个。
  • 加把劲,脱贫致富奔小康。
  • 多在意些在意你的人,如此类推,多关心些关心你的人。
  • 持之以恒,精神会在物质之前富裕起来。

状态

  • 工作:老老实实完成份内之事,尽量不偷懒。此外坚持四项基本原则:不迟到,不早退,不旷工,不加班。
  • 学习:每天坚持看2个小时视频材料,尽量从工作和休息时间挤一个小时看相关文档并完成代码。
  • 抽烟:一天一包->三天一包->一周一包,继续努力,创造无烟生活。

关于博客搬家

Filed Under (博客聚合) by panmaoru on 02-11-2009

Colsir.com搬到国外已经一个星期了,运行平稳,在此称赞一下WordPress:功能完善,模板丰富以及大无畏的开源思想。Colsir之前的asp.Net程序是自己开发的,从第一版本开始就落下很多毛病,一直到当机也没解决,因为年前的小冲动跑到hostmonster买了台虚拟主机,这次smoile的服务器aspx配置出了问题之后才狠下心:搬家。

去年国庆的是很xena让我给他写一个WordPress的辅助程序,当时在网上看了一些关于Metaweblog api的文章,自己参照着示例做实验的时候遇到了一个很繁琐的异常处理问题没能解决,后来接了个OA的项目,就把这件事情耽搁了。前段时间这件事情又被提起来,便认真的看了下ZJ那篇 使用Windows Live Spaces MetaWeblog API 的文章,自己写了个小程序把colsir.com文章搬了出来。

Colsir原先的数据库是Access的,逐条从数据库读日志信息,然后用MS的Windows Live Spaces MetaWeblog API,将日志写入到WordPress中。搬之前查了下MetaWeblog API的结构,只有日志和分类的的接口,于是不得不放弃原先的评论信息,遗憾的很。

readonly M_MetaWeblog m_blog = new M_MetaWeblog ();
m_blog.Url ="http://www.colsir.com/xmlrpc.php ";
Post newPost = new Post ();
//做个循环,开始发布日志
newPost.dateCreated = "时间";//时间格式
newPost.title = "标题";
newPost.description = "内容";
newPost.categories = new[] { "C1", "C2" };
m_blog.newPost ( "_blogid", "UserName", "PWD", newPost, true );

40多篇文章,大概几秒钟就发布完成了,速度极其快。目前已经有不少离线发布blog的工具软件,好多BSP也公开了API,有一个泛滥的时代啊!

今天上午看了一些Post/Get机制的文章,回头再看看MetaWeblog API的消息结构,貌似原理上相通的很,想在网络这行混,HTTP协议得恶补。

挺困的

Filed Under (随网心情) by panmaoru on 30-10-2009

被鄙视了数次,总算找了个落脚的地方。一个中国人在加拿大开了软件外包公司基于成本的考虑于是到南京办了分公司,我有幸成为公司的第一批员工,上了一个星期班,除了困,别的没感觉,每天都要早起上班,很折腾。

新公司,新项目,一切都要照软件开发的流程来走,于是每天都在看文档,写文档,一个开发不要1周的asp.net项目,光文档折腾了一个星期,我土鸡,理解不了。心情浮躁的时候常想到《疯狂的石头》里面那句台词“你直接上不就得了,费那事干嘛”,不过也会立刻想到另外一句“素质,注意素质”,这应该就是我长久以来土,不等大雅的原因之一。

工作的压力并不大,于是闲着的时候便看看《C++ primer》洗洗脑子,或者是pdf制作者的用心良苦吧,文字不能复制,仿佛告诉我:你懒,你再懒明年还被鄙视。想测试代码示例的时候不得不一个个敲出来,效果还不错。我这只小蜗牛,明年能爬到哪?

冷冷清清凄凄惨惨戚戚

Filed Under (随网心情) by panmaoru on 13-07-2009

放下很久,想要动手的时候如此艰难;

压抑,空洞,迷惘,此刻涌上心头的到是很丰富;

有些冷清,更冷清;

还能更悲剧些嘛?

这并不公平,至少,此刻;

今天过去,明天就来了;

经历的,抹不去,逃不掉;

轻轻的,轻轻的,轻轻的放下,轻轻的捡起。。。