<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[观夏Note]]></title> 
<link>//gm.angeldm.com/index.php</link> 
<description><![CDATA[新技术番]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[观夏Note]]></copyright>
<item>
<link>//gm.angeldm.com/post/223/</link>
<title><![CDATA[如何定制对话框系统菜单（C++/MFC）]]></title> 
<author>果面 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[程序开发]]></category>
<pubDate>Tue, 21 Feb 2012 14:57:50 +0000</pubDate> 
<guid>//gm.angeldm.com/post/223/</guid> 
<description>
<![CDATA[ 
	【简 介】<br/>系统菜单是每个 Windows 程序的标准特性。通常系统菜单由 Windows 系统来管理，所以我们平时编成时很少去碰它。但是，有的时候，我们确实想定制自己的系统菜单项。这样就涉及到定制菜单的处理问题，因为 Windows 无法自动处理我们定制的系统菜单。而且，系统菜单的处理方式与常规的菜单处理是不同的。那么我们如何实现定制的系统菜单呢？相信看完本文的介绍，你会得到满意的答案。<br/><br/> <br/><br/>本文例子是一个典型的C++/MFC对话框程序，设置了 EX_WM_TOOLWINDOW 扩展式样，因此在标题栏左上角看不到系统菜单图标，但通过 Ctrl+Space 或者在标题栏单击鼠标右键可以调出系统菜单。例子程序对系统菜单进行了定制，在原有菜单基础上添加了两个菜单命令：一个是显示“关于”对话框；另一个是“退出”。之所以要加一个“退出”菜单命令，是因为例子程序改写了对话框原来默认的“关闭”菜单命令行为（Alt-F4），用来隐藏对话框。因此必须加一个程序的“退出”出口。此外，例子程序利用一个第三方的系统托盘处理类，利用系统托盘图标可以显示/隐藏对话框。 下面我们就来看看用 C++/MFC 实现的细节。<br/><br/><br/>添加菜单<br/><br/>首先在资源定义文件 resource.h 中定义菜单项标示，也可以在标准头文件中定义。菜单项标示必须具有唯一性。其次，Windows 对系统菜单的处理与常规菜单的处理方法是不同的，不管是缺省的菜单还是定制的菜单，它们都没有象常规菜单命令那样的消息处理例程。假设我们要添加两个定制的系统单：<br/><div class="code"><br/>#define IDM_ABOUT 16<br/>#define IDM_EXIT 17<br/>IDM_的意思是该定义为菜单项ID。添加菜单命令是在对话框的初始化例程以及窗口创建函数（OnInitDialog(), OnCreate()）中进行的。如： BOOL CBabelOnDlg::OnInitDialog()<br/>&#123;<br/>CDialog::OnInitDialog();<br/><br/>// 在系统菜单中添加 &quot;关于...&quot; 和 &quot;退出&quot; 菜单项<br/><br/><br/>// 解决 Windows 95 中的 bug<br/>ASSERT((IDM_ABOUTBOX amp; 0xFFF0) == IDM_ABOUTBOX);<br/><br/>// 命令 IDs 必须在预定义的系统菜单之后<br/>ASSERT(IDM_ABOUTBOX &lt; 0xF000);<br/><br/>// 解决 Windows 95 中的 bug<br/>ASSERT((IDM_EXIT amp; 0xFFF0) == IDM_EXIT);<br/><br/>// 命令 IDs 必须在预定义的系统菜单之后<br/>ASSERT(IDM_EXIT &lt; 0xF000);<br/><br/>CMenu* pSysMenu = GetSystemMenu(FALSE);<br/>if (pSysMenu != NULL)<br/>&#123;<br/>pSysMenu-&gt;AppendMenu(MF_STRING,IDM_EXIT,&quot;退出(amp;x)&quot;);<br/>pSysMenu-&gt;AppendMenu(MF_SEPARATOR);<br/>pSysMenu-&gt;AppendMenu(MF_STRING, IDM_ABOUTBOX, &quot;关于(amp;A)...&quot;);<br/>......<br/>&#125;<br/><br/>......<br/><br/>//other initialization<br/>&#125;<br/></div><br/>　　这里在添加每个菜单前都有两个 ASSERT 语句，第一个 ASSERT 的目的是修复 Windows 95 中存在的 Bug，第二个 ASSERT 保证定制的命令 IDs 是在预定义的系统菜单之后，以免发生冲突。查一下 MSDN 库的 MFC 文档关于系统菜单的描述，你会发现下面的内容： “......所有预定义的控制菜单项（也就是系统菜单）的ID号必须大于 0xF000。如果某个应用程序要添加系统菜单，<br/>其系统菜单的 ID 号必须小于F000。”<br/>接下来，用 GetSystemMenu 函数获取系统菜单指针。调用时使用参数 FALSE 获取指针。如果用 TRUE 作为参数，那么该函数会将菜单重置回缺省状态。<br/>如果得到的指针有效，接着调用菜单添加命令在系统菜单后面添加菜单项，传递菜单IDs以及菜单显示时所用的字符串。<br/><br/>处理定制的菜单命令<br/><br/>为了让这些系统菜单命令工作起来，我们不能依赖常规的菜单消息处理机制----即便菜单项相同。通常系统菜单通过 WM_SYSCOMMAND 消息处理：<br/><div class="code"><br/> void CBabelOnDlg::OnSysCommand(UINT nID, LPARAM lParam)<br/>&#123;<br/>//trap our own system menu messages<br/>if ((nID amp; 0xFFF0) == IDM_ABOUTBOX)<br/>&#123;<br/>CAboutDlg dlgAbout;<br/>dlgAbout.DoModal();<br/>&#125; else if ((nID amp; 0xFFF0)==SC_CLOSE)&#123;<br/>OnClose();<br/>&#125; else if ((nID amp; 0xFFF0)==IDM_EXIT) &#123;<br/>::PostQuitMessage(0);<br/>&#125;<br/>else &#123;<br/>CDialog::OnSysCommand(nID, lParam);<br/>&#125;<br/>&#125;<br/></div><br/>　　通过比较传入的菜单ID进行相应的处理。注意代码中又有两个“nID amp; 0xFFF0”，这主要也是解决 Windows 95 的 bug。如果选择“退出”，那么会向应用程序发送退出消息：::PostQuitMessage(0)。<br/>注意第二个条件检查：SC_CLOSE 是个预定义的菜单常量。一般它是由 Windows 处理的，因为在例子程序中我们对它进行了定制，所以必须要自己处理它。本来 SC_CLOSE 是退出程序，但例子程序我们把它的行为改写成隐藏对话框，也就是将应用变成一个托盘小图标，处理例程见 OnClose() 函数。如果传入的菜单ID不等于任何定制的菜单项，那么就让 Windows 对它进行默认处理： CDialog::OnSysCommand(nID, lParam);<br/>下面是几个最常用的系统菜单命令：<br/>　菜单 说明<br/><div class="code"><br/>SC_CLOSE 关闭 CWnd 对象<br/>SC_MAXIMIZE 或者 SC_ZOOM 最大化 CWnd 对象<br/>SC_MINIMIZE 或者 SC_ICON 最小化 CWnd 对象<br/>SC_MOVE 移动 CWnd 对象<br/>SC_RESTORE 恢复窗口的正常位置和大小<br/>SC_SIZE 改变 CWnd 对象大小<br/></div><br/>其它的几个系统菜单命令一般都是在特殊情况下才使用，有关细节请参考有关 WM_SYSCOMMAND 的文档。<br/><br/>修改现有的菜单命令<br/><br/>我们已经看到，系统菜单本身默认的处理行为是可以改变的，除此之外，系统菜单项的描述文本也是可以改变的，甚至还可以删除它们。为了修改才单命令的描述文本，我们可以用 pSysMenu 指针调用 ModifyMenu() 函数。例如，如果想要把“关闭”菜单项改成“隐藏”，可以象下面这么做：<br/><div class="code"><br/>pSysMenu-&gt;ModifyMenu(SC_CLOSE, MF_BYCOMMAND,IDM_HIDE, &quot;隐藏(amp;H)&quot;);<br/>MF_BYCOMMAND 参数告诉该函数将 SC_CLOSE 解释为命令 ID。IDM_HIDE 是新的菜单 ID。最后一个参数是菜单项的说明文本。还有一种调用 ModifyMenu() 的方法是使用 菜单项索引作为参数： pSysMenu-&gt;ModifyMenu(0,MF_BYPOSITION,IDM_HIDE,&quot;隐藏(amp;H)&quot;);<br/>第一个参数 0 表示菜单项的索引，指第一个菜单。<br/></div><br/>删除菜单命令<br/><br/>例子程序拟将去掉系统菜单中的窗口“关闭”命令，暂且不说这样做是否合适，但是我们能做到这一点： <br/><div class="code"><br/>pSysMenu-&gt;RemoveMenu(SC_CLOSE,MF_BYCOMMMAND);<br/>pSysMenu-&gt;RemoveMenu(0,MF_BYPOSITION);<br/></div><br/>第一行代码删除了与 SC_CLOSE 关联的菜单命令。而第二行代码表示删除系统菜单命令中的第一项。<br/><br/>用这种方式修改系统菜单尽管限定了应用程序的某些行为，但对于小型应用和实用程序来说有时是很有用的，尤其是当你想要从任务栏存取菜单命令时----也就是程序在后台运行或者以最小化方式运行，右键单击任务栏图标将弹出系统菜单。<br/>
]]>
</description>
</item><item>
<link>//gm.angeldm.com/post/222/</link>
<title><![CDATA[mfc 小程序---在系统菜单中添加菜单项]]></title> 
<author>果面 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[程序开发]]></category>
<pubDate>Tue, 21 Feb 2012 14:53:29 +0000</pubDate> 
<guid>//gm.angeldm.com/post/222/</guid> 
<description>
<![CDATA[ 
	1建立一个对话框工程；在dlg类里定义一个菜单指针m_pMenu，在对话框OnInitDialog函数里添加代码：<br/><div class="code"><br/>m_pMenu=GetSystemMenu(FALSE);//获取系统菜单的指针<br/>m_pMenu-&gt;AppendMenu(MF_SEPARATOR);//添加分割线<br/>m_pMenu-&gt;AppendMenu(MF_STRING,IDI_PECULIARMENU,&quot;系统菜单&quot;);<br/></div><br/><br/>补充：IDI_PECULIARMENU 为一个常数，需要在Resourece.h中宏定义：<br/><div class="code"><br/>#define IDI_PECULIARMENU&nbsp;&nbsp;1201<br/></div><br/>2在 OnSysCommand 函数中增加代码：<br/><div class="code"><br/>else if(nID==IDI_PECULIARMENU)<br/> &#123;<br/>&nbsp;&nbsp;MessageBox(&quot;系统菜单&quot;,&quot;提示&quot;,MB_OK&#124;MB_ICONINFORMATION);<br/> &#125;<br/></div><br/>扩展：在系统菜单里添加背景图片，为标题添加图标<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp; 1 ：加载bitmap 图片IDB_MAIN<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp; 在onPaint函数中添加如下代码：<br/><div class="code"><br/> CDC*m_dc=this-&gt;GetDC();<br/> CDC m_memdc;<br/> m_memdc.CreateCompatibleDC(m_dc);<br/> CBitmap m_bitmap;<br/> m_bitmap.LoadBitmap(IDB_MAIN);<br/> m_memdc.SelectObject(&amp;m_bitmap);<br/> CRect m_rect;<br/> m_dc-&gt;BitBlt(0,0,800,700,&amp;m_memdc,0,0,SRCCOPY);<br/> m_bitmap.DeleteObject();<br/></div><br/>2:加载icon 图片作为标题栏图标：IDI_MAINMENU<br/><br/>之后直接更改m_hIcon的值即可：<br/><div class="code"><br/>m_hIcon = AfxGetApp()-&gt;LoadIcon(IDI_MAINMENU);<br/></div><br/>补充：如果是外部应用程序首先FindWindow()找到窗口<br/>向窗口发送&nbsp;&nbsp; WM_SECTION消息。<br/><div class="code"><br/>&nbsp;&nbsp;HICON&nbsp;&nbsp; hIcon=AfxGetApp()-&gt; LoadIcon(IDI_YOUR_ICON)<br/>&nbsp;&nbsp;AfxGetMainWnd()-&gt; SendMessage(WM_SECTION,TRUE,(LPARAM)hIcon)<br/></div><br/>最后这个现在还没试过……
]]>
</description>
</item><item>
<link>//gm.angeldm.com/post/221/</link>
<title><![CDATA[MFC之托盘图标]]></title> 
<author>果面 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[程序开发]]></category>
<pubDate>Tue, 21 Feb 2012 14:00:31 +0000</pubDate> 
<guid>//gm.angeldm.com/post/221/</guid> 
<description>
<![CDATA[ 
	在VC++中,想实现最小化MFC程序的时候,最小化到系统托盘,需要调用NOTIFYICONDATA类<br/><br/>下面我们就来讲解一下如何简单实现一个系统托盘 我们以对话框程序为列<br/><br/>第一步：在Dlg类中//定义一个NOTIFYICONDATA类的成员变量,用来设置托盘<br/><div class="code"><br/>NOTIFYICONDATA NotifyIcon;<br/></div><br/>第二步：声明一个消息响应函数<br/><div class="code"><br/>afx_msg void OnNotifyIcon(WPARAM wParam,LPARAM IParam);<br/></div><br/><br/>上面那条代码也放在Dlg的头文件中<br/><br/>第三步：定义一个自定义消息<br/><div class="code"><br/>#define WM_NC WM_USER+1<br/></div><br/>上面那条代码也在Dlg的头文件中声明<br/><br/>注册消息<br/><div class="code"><br/>ON_MESSAGE(WM_NC,OnNotifyIcon)<br/></div><br/>上面那条代码在Dlg类中<br/><br/>第四步：在Dlg头文件中添加一个函数 用来响应点击最小化按钮<br/><div class="code"><br/>void changeMini();<br/></div><br/>并在Dlg类中实现这个函数<br/><div class="code"><br/>//响应最小化消息<br/>void CChangeScreenDlg::changeMini()<br/>&#123;<br/>ShowWindow(SW_HIDE);<br/>NotifyIcon.cbSize=sizeof(NOTIFYICONDATA);<br/>NotifyIcon.hIcon=AfxGetApp()-&gt;LoadIcon(IDR_MAINFRAME);<br/>NotifyIcon.hWnd=m_hWnd;<br/>lstrcpy(NotifyIcon.szTip,&quot;MediaCraft EMMG&quot;);<br/>NotifyIcon.uCallbackMessage=WM_NC;<br/>NotifyIcon.uFlags=NIF_ICON &#124; NIF_MESSAGE &#124; NIF_TIP;<br/>Shell_NotifyIcon(NIM_ADD,&amp;NotifyIcon);<br/>&#125;<br/></div><br/>第五步：截获最小化事件<br/><br/>在Dlg类中找到OnSysCommand函数<br/><br/>修改代码如下<br/><div class="code"><br/>if ((nID &amp; 0xFFF0) == IDM_ABOUTBOX)<br/>&#123;<br/>&nbsp;&nbsp; CAboutDlg dlgAbout;<br/>&nbsp;&nbsp; dlgAbout.DoModal();<br/>&#125;<br/>//拦截最小化事件<br/>if(nID==SC_MINIMIZE)<br/>&#123;<br/>&nbsp;&nbsp; //ToTray();<br/><br/>//用我们自己的消息响应最小化事件<br/>&nbsp;&nbsp; changeMini();<br/>&#125;<br/>else<br/>&#123;<br/>&nbsp;&nbsp; CDialog::OnSysCommand(nID, lParam);<br/>&#125;<br/></div><br/>第六步：响应托盘的鼠标事件 也是实现我们第二部声明的消息响应函数<br/><div class="code"><br/>void CChangeScreenDlg::OnNotifyIcon(WPARAM wParam,LPARAM IParam)<br/>&#123;<br/>if ((IParam == WM_LBUTTONDOWN) &#124;&#124; (IParam == WM_RBUTTONDOWN))<br/>&#123;<br/>ModifyStyleEx(0,WS_EX_TOPMOST);<br/>ShowWindow(SW_SHOW);<br/>&#125;<br/>&#125;<br/></div><br/>最后一步：在类的析构函数中 记得销毁托盘图标对象<br/><div class="code"><br/>Shell_NotifyIcon(NIM_DELETE, &amp;NotifyIcon);<br/></div><br/>以上NOTIFYICONDATA类的一些具体参数，请参阅MSDN<br/>
]]>
</description>
</item><item>
<link>//gm.angeldm.com/post/220/</link>
<title><![CDATA[MFC：怎么将程序窗口最小化到系统托盘]]></title> 
<author>果面 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[程序开发]]></category>
<pubDate>Tue, 21 Feb 2012 13:56:59 +0000</pubDate> 
<guid>//gm.angeldm.com/post/220/</guid> 
<description>
<![CDATA[ 
	（一）原理<br/><br/>1、最小化的原理：首先要将窗口隐藏，然后在右下角绘制图标。<br/><br/>2、恢复的原理：将窗口显示，再将托盘中的图片删除。<br/><br/>（二）程序实现<br/><br/>1、自定义消息WM_SHOWTASK：<br/><div class="code"><br/>#define WM_SHOWTASK (WM_USER +1)<br/></div><br/>2、在MFC的::OnSysCommand(UINT nID, LPARAM lParam)&nbsp;&nbsp; 函数体中增加一个命令响应<br/><div class="code"><br/>if(nID==SC_MINIMIZE)<br/>ToTray();&nbsp;&nbsp;//最小化到托盘的函数<br/></div><br/><br/>3、在消息映射中添加<br/><div class="code"><br/>ON_MESSAGE(WM_SHOWTASK,OnShowTask);&nbsp;&nbsp;//其中WM_SHOWTASK是消息名，<br/></div><br/>OnShowTask是自己定义的消息响应函数，后面有说明。<br/><br/>（三）具体函数内容<br/><br/>1、最小化到托盘函数<br/><div class="code"><br/>void CMyDlg::ToTray()<br/><br/>&#123;<br/><br/>NOTIFYICONDATA nid;<br/><br/>nid.cbSize=(DWORD)sizeof(NOTIFYICONDATA);<br/><br/>nid.hWnd=this-&gt;m_hWnd;<br/><br/>nid.uID=IDR_MAINFRAME;<br/><br/>nid.uFlags=NIF_ICON&#124;NIF_MESSAGE&#124;NIF_TIP ;<br/><br/>nid.uCallbackMessage=WM_SHOWTASK;//自定义的消息名称<br/><br/>nid.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));<br/><br/>strcpy(nid.szTip,”程序名称”);&nbsp;&nbsp;&nbsp;&nbsp;//信息提示条<br/><br/>Shell_NotifyIcon(NIM_ADD,&amp;nid);&nbsp;&nbsp;&nbsp;&nbsp;//在托盘区添加图标<br/><br/>ShowWindow(SW_HIDE);&nbsp;&nbsp;&nbsp;&nbsp;//隐藏主窗口<br/><br/>&#125;<br/></div><br/>2、恢复界面函数<br/><br/>在头文件中定义消息响应函数<br/><div class="code"><br/>afx_msg LRESULT OnShowTask(WPARAM wParam,LPARAM lParam) ;<br/><br/>//wParam接收的是图标的ID，而lParam接收的是鼠标的行为<br/><br/>LRESULT CMyDlg::OnShowTask(WPARAM wParam,LPARAM lParam)<br/><br/>&#123;<br/><br/>if(wParam!=IDR_MAINFRAME)<br/><br/>return 1;<br/><br/>switch(lParam)<br/><br/>&#123;<br/><br/>case WM_RBUTTONUP://右键起来时弹出快捷菜单，这里只有一个“关闭”<br/><br/>&#123;<br/><br/>LPPOINT lpoint=new tagPOINT;<br/><br/>::GetCursorPos(lpoint);//得到鼠标位置<br/><br/>CMenu menu;<br/><br/>menu.CreatePopupMenu();//声明一个弹出式菜单<br/><br/>menu.AppendMenu(MF_STRING,WM_DESTROY,”关闭”); //增加菜单项“关闭”，点击则发送消息WM_DESTROY给主窗口（已隐藏），将程序结束。<br/><br/>menu.TrackPopupMenu(TPM_LEFTALIGN,lpoint-&gt;x,lpoint-&gt;y,this); //确定弹出式菜单的位置<br/><br/>HMENU hmenu=menu.Detach();<br/><br/>menu.DestroyMenu(); //资源回收<br/><br/>delete lpoint;<br/><br/>&#125;&nbsp;&nbsp;break;<br/><br/>case WM_LBUTTONDBLCLK:&nbsp;&nbsp; //双击左键的处理<br/><br/>&#123;<br/><br/>this-&gt;ShowWindow(SW_SHOW);//简单的显示主窗口完事儿<br/><br/>DeleteTray();<br/><br/>&#125;&nbsp;&nbsp;break;<br/><br/>default:&nbsp;&nbsp; break;<br/><br/>&#125;<br/><br/>return 0;<br/><br/>&#125;<br/></div><br/>3、删除托盘图标函数<br/><div class="code"><br/>void CMyDlg::DeleteTray()<br/><br/>&#123;<br/><br/>NOTIFYICONDATA nid;<br/><br/>nid.cbSize=(DWORD)sizeof(NOTIFYICONDATA);<br/><br/>nid.hWnd=this-&gt;m_hWnd;<br/><br/>nid.uID=IDR_MAINFRAME;<br/><br/>nid.uFlags=NIF_ICON&#124;NIF_MESSAGE&#124;NIF_TIP ;<br/><br/>nid.uCallbackMessage=WM_SHOWTASK;&nbsp;&nbsp; //自定义的消息名称<br/><br/>nid.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));<br/><br/>strcpy(nid.szTip,”程序名称”);&nbsp;&nbsp;&nbsp;&nbsp;//信息提示条为“计划任务提醒”<br/><br/>Shell_NotifyIcon(NIM_DELETE,&amp;nid);&nbsp;&nbsp;&nbsp;&nbsp;//在托盘区删除图标<br/><br/>&#125;<br/></div>
]]>
</description>
</item><item>
<link>//gm.angeldm.com/post/219/</link>
<title><![CDATA[MFC应用程序创建窗口过程和关闭窗口顺序]]></title> 
<author>果面 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[程序开发]]></category>
<pubDate>Tue, 21 Feb 2012 13:48:29 +0000</pubDate> 
<guid>//gm.angeldm.com/post/219/</guid> 
<description>
<![CDATA[ 
	创建非模态窗口过程<br/><br/>1.PreCreateWindow() 该函数是一个重载函数，<span style="color: #4169E1;">在窗口被创建前，可以在该重载函数中改变创建参数(可以设置窗口风格等等)</span><br/><br/>2.PreSubclassWindow() 这也是一个重载函数，<span style="color: #4169E1;">允许首先子分类一个窗口</span><br/><br/>3.OnGetMinMaxInfo() 该函数为消息响应函数，响应的是<span style="color: #FF0000;">WM_GETMINMAXINFO</span>消息，<span style="color: #4169E1;">允许设置窗口的最大或者最小尺寸</span><br/><br/>4.OnNcCreate()该函数也是一个消息响应函数，响应<span style="color: #FF0000;">WM_NCCREATE</span>消息，<span style="color: #4169E1;">发送消息以告诉窗口的客户区即将被创</span><br/><br/>5.OnNcCalcSize() 该函数也是消息响应函数,响应<span style="color: #FF0000;">WM_NCCALCSIZE</span>消息，<span style="color: #4169E1;">作用是允许改变窗口客户区大小</span><br/><br/>6.OnCreate()&nbsp;&nbsp;该函数也是一个消息响应函数，响应<span style="color: #FF0000;">WM_CREATE</span>消息，<span style="color: #4169E1;">发送消息告诉一个窗口已经被创建</span><br/><br/>7.OnSize() 该函数也是一个消息响应函数，响应<span style="color: #FF0000;">WM_SIZE</span>消息，<span style="color: #4169E1;">发送该消息以告诉该窗口大小已经发生变化</span><br/><br/>8.OnMove() 消息响应函数，响应<span style="color: #FF0000;">WM_MOVE</span>消息，<span style="color: #4169E1;">发送此消息说明窗口在移动</span><br/><br/>9.OnChildNotify()该函数为重载函数，作为部分消息映射被调用，<span style="color: #4169E1;">告诉父窗口即将被告知一个窗口刚刚被创建</span><br/><br/>创建一个非模态窗口<br/><br/>CTestDlg *pDlg=new CTestDlg;<br/><br/>pDlg->Create(IDD_TESTDLG,this);<br/><br/>pDlg->ShowWindow(SW_SHOW);<br/><br/> <br/><br/>非模态窗口关闭顺序<br/><br/>1.OnClose() 消息响应函数，响应窗口的<span style="color: #FF0000;">WM_CLOSE</span>消息，<span style="color: #4169E1;">当关闭按钮被单击的时候发送此消息</span><br/><br/>2.OnDestroy()消息响应函数，响应窗口的<span style="color: #FF0000;">WM_DESTROY</span>消息，<span style="color: #4169E1;">当一个窗口将被销毁时，发送此消息</span><br/><br/>3.OnNcDestroy() 消息响应函数，响应窗口的<span style="color: #FF0000;">WM_NCDESTROY</span>消息，<span style="color: #4169E1;">当一个窗口被销毁后发送此消息</span><br/><br/>4.PostNcDestroy() 重载函数，作为处理<span style="color: #FF0000;">OnNcDestroy()</span>函数的最后动作，<span style="color: #4169E1;">被CWnd调用</span><br/><br/>下述几个实际 的关闭过程：<br/><br/>OnClose()->OnCancel()->DestroyWindow()->OnDestroy()->OnNcDestroy()<br/><br/>OnCancel()->DestroyWindow()->OnDestroy()->OnNcDestroy()&nbsp;&nbsp; （IDCANCEL）<br/><br/>OnOK()->DestroyWindow()->OnDestroy()->OnNcDestroy()&nbsp;&nbsp; （ID_OK）<br/><br/><span style="font-size: 12px;"><span style="color: #FF0000;">注意：</span></span>在一个模块或者一个函数中创建窗口，无法知道什么时候关闭窗口。而pWnd也只是作为一个局部变量，要对它进行析构。一般操作如下：<br/><br/>void CTestDlg::OnCancel()<br/>&#123;<br/>&nbsp;&nbsp;DestroyWindow();<br/>&#125;<br/>void CTestDlg::PostNcDestroy()<br/>&#123;<br/>&nbsp;&nbsp;CDialog::PostNcDestroy();<br/>&nbsp;&nbsp;delete this;<br/>&#125;<br/><br/><br/>创建模态窗口过程<br/><br/>1.DoModal() 重载函数，重载DoModal()成员函数<br/><br/>2.PreSubclassWindow() 重载函数，允许首先子分类一个窗口<br/><br/>3.OnCreate() 消息响应函数，响应<span style="color: #FF0000;">WM_CREATE</span>消息，发送此消息以告诉一个窗口已经被创建<br/><br/>4.OnSize()消息响应函数，响应<span style="color: #FF0000;">WM_SIZE</span>消息，发送此消息以告诉窗口大小发生变化<br/><br/>5.OnMove()消息响应函数，响应<span style="color: #FF0000;">WM_MOVE</span>消息，发送此消息，以告诉窗口正在移动<br/><br/>6.OnSetFont()消息响应函数，响应<span style="color: #FF0000;">WM_SETFONT</span>消息，发送此消息，以允许改变对话框中控件的字体<br/><br/>7.OnInitDialog()消息响应函数，响应<span style="color: #FF0000;">WM_INITDIALOG</span>消息，发送此消息以允许初始化对话框中的控件，或者是创建新控件<br/><br/>8.OnShowWindow()消息响应函数，响应<span style="color: #FF0000;">WM_SHOWWINDOW</span>消息，该函数被ShowWindow()函数调用<br/><br/>9.OnCtlColor()&nbsp;&nbsp;消息响应函数，响应<span style="color: #FF0000;">WM_CTLCOLOR</span>消息，被父窗口发送已改变对话框或对话框上面控件的颜色<br/><br/>10. OnChildNotify()重载函数，作为<span style="color: #FF0000;">WM_CTLCOLOR</span>消息的结果发送<br/><br/>创建：<br/><br/>CMyModalDialog dlg;<br/>dlg.DoModal();<br/><br/>模态窗口关闭顺序<br/><br/>1.OnClose()消息响应函数，响应<span style="color: #FF0000;">WM_CLOSE</span>消息，当"关闭"按钮被单击的时候，该函数被调用<br/><br/>2.OnKillFocus()&nbsp;&nbsp;消息响应函数，响应<span style="color: #FF0000;">WM_KILLFOCUS</span>消息，当一个窗口即将失去键盘输入焦点以前被发送<br/><br/>3.OnDestroy() 消息响应函数，响应<span style="color: #FF0000;">WM_DESTROY</span>消息，当一个窗口即将被销毁时，被发送<br/><br/>4.OnNcDestroy()&nbsp;&nbsp;消息响应函数，响应<span style="color: #FF0000;">WM_NCDESTROY</span>消息，当一个窗口被销毁以后被发送<br/><br/>5.PostNcDestroy()重载函数，作为处理<span style="color: #FF0000;">OnNcDestroy()</span>函数的最后动作被CWnd调用
]]>
</description>
</item><item>
<link>//gm.angeldm.com/post/218/</link>
<title><![CDATA[MFC下托盘图标的实现和托盘菜单]]></title> 
<author>果面 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[程序开发]]></category>
<pubDate>Tue, 21 Feb 2012 13:20:18 +0000</pubDate> 
<guid>//gm.angeldm.com/post/218/</guid> 
<description>
<![CDATA[ 
	对话框头文件XXXDlg.h:<br/><br/>1.添加成员变量<br/><div class="code">NOTIFYICONDATA m_nid;</div><br/><br/>2.添加tray消息响应函数的声明<br/><br/><div class="code">afx_msg LRESULT OnTrayNotify(WPARAM wParam, LPARAM lParam);</div><br/><br/>对话框实现文件XXXDlg.cpp:<br/><br/>1.定义tray消息<br/><div class="code">#define UM_TRAYNOTIFY WM_USER + 11</div><br/><br/>2.CXXXDlg的构造函数添加<br/><div class="code"><br/> memset(&amp;m_nid, 0, sizeof(m_nid)); // Initialize NOTIFYICONDATA struct<br/> m_nid.cbSize = sizeof(m_nid);<br/> m_nid.uFlags = NIF_ICON &#124; NIF_TIP &#124; NIF_MESSAGE;<br/></div><br/>3.CXXXDlg的析构函数添加<br/><div class="code"><br/> m_nid.hIcon = NULL;<br/> Shell_NotifyIcon(NIM_DELETE, &amp;m_nid);<br/></div><br/>4.添加消息映射：<br/><div class="code"><br/>BEGIN_MESSAGE_MAP(CMFC2Dlg, CDialog)<br/> //...<br/> ON_MESSAGE(UM_TRAYNOTIFY, &amp;CMFC2Dlg::OnTrayNotify)<br/> //...<br/>END_MESSAGE_MAP() <br/></div><br/>5.OnInitDialog函数添加：<br/><div class="code"><br/>m_nid.hWnd = GetSafeHwnd();<br/> m_nid.uCallbackMessage = UM_TRAYNOTIFY;<br/><br/> // Set tray icon and tooltip<br/> m_nid.hIcon = m_hIcon;<br/><br/> // Set tray notification tip information<br/> CString strToolTip = _T(&quot;托盘程序&quot;);<br/> _tcsncpy_s(m_nid.szTip, strToolTip, strToolTip.GetLength());<br/> Shell_NotifyIcon(NIM_ADD, &amp;m_nid);<br/></div><br/>6.OnTrayNotify函数的实现：<br/><div class="code"><br/>LRESULT CXXXDlg::OnTrayNotify(WPARAM wParam, LPARAM lParam)<br/>&#123;<br/> UINT uMsg = (UINT)lParam;<br/><br/> switch(uMsg)<br/> &#123;<br/> case WM_RBUTTONUP:<br/>&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp; //右键处理<br/>&nbsp;&nbsp;CMenu menuTray;<br/>&nbsp;&nbsp;CPoint point;<br/>&nbsp;&nbsp;int id;<br/>&nbsp;&nbsp;GetCursorPos(&amp;point);<br/>&nbsp;&nbsp;<br/>&nbsp;&nbsp;menuTray.LoadMenu(IDR_MENU_TRAY);<br/>&nbsp;&nbsp;id = menuTray.GetSubMenu(0)-&gt;TrackPopupMenu(TPM_RETURNCMD &#124; TPM_LEFTALIGN&#124;TPM_RIGHTBUTTON, point.x, point.y, this);<br/>#if 0<br/>&nbsp;&nbsp;CString strInfo;<br/>&nbsp;&nbsp;strInfo.Format(L&quot;menuid %d&quot;, id);<br/>&nbsp;&nbsp;LPCTSTR strtmp;<br/>&nbsp;&nbsp;strtmp = strInfo.GetBuffer(0);<br/>&nbsp;&nbsp;MessageBox(strtmp, L&quot;test&quot;);<br/>#endif<br/>&nbsp;&nbsp;switch(id)&#123;<br/>&nbsp;&nbsp; case IDR_TRAY_EXIT:<br/>&nbsp;&nbsp;&nbsp;&nbsp;OnOK();<br/>&nbsp;&nbsp;&nbsp;&nbsp;break;<br/>&nbsp;&nbsp; case IDR_TRAY_RESTORE:<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;//窗口前端显示<br/>&nbsp;&nbsp;&nbsp;&nbsp;SetForegroundWindow();<br/>&nbsp;&nbsp;&nbsp;&nbsp;ShowWindow(SW_SHOWNORMAL);<br/>&nbsp;&nbsp;&nbsp;&nbsp;break;<br/>&nbsp;&nbsp; default:<br/>&nbsp;&nbsp;&nbsp;&nbsp;break;<br/>&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;break;<br/>&nbsp;&nbsp;&#125;<br/> case WM_LBUTTONDBLCLK:<br/>&nbsp;&nbsp;SetForegroundWindow();<br/>&nbsp;&nbsp;ShowWindow(SW_SHOWNORMAL);<br/>&nbsp;&nbsp;break;<br/> default:<br/>&nbsp;&nbsp;break;<br/> &#125;<br/> return 0;<br/>&#125;<br/></div><br/>7.添加WM_SIZE消息处理：<br/><div class="code"><br/>void CMFC2Dlg::OnSize(UINT nType, int cx, int cy)<br/>&#123;<br/> CDialog::OnSize(nType, cx, cy);<br/><br/> if(nType == SIZE_MINIMIZED)&#123;<br/>&nbsp;&nbsp;ShowWindow(SW_HIDE);<br/> &#125;<br/>&#125;<br/></div><br/>8.菜单。<br/><br/>添加菜单资源 , 比如IDR_MENU_TRAY.<br/><br/>定义一个子菜单Tray，有若干个菜单项，比如“恢复窗口”, ID是IDR_TRAY_RESTORE，“退出”, ID是IDR_TRAY_EXIT。<br/><br/>在OnTrayNotify函数中捕获右击消息，弹出菜单，参考第6步。<br/><br/> <br/><br/>至此，添加托盘的功能基本完成。<br/>
]]>
</description>
</item><item>
<link>//gm.angeldm.com/post/214/</link>
<title><![CDATA[C++中cos，sin，tan等三角函数角度转弧度]]></title> 
<author>果面 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[程序开发]]></category>
<pubDate>Fri, 02 Dec 2011 02:31:21 +0000</pubDate> 
<guid>//gm.angeldm.com/post/214/</guid> 
<description>
<![CDATA[ 
	C++中cos,sin,asin,acos这些三角函数操作的是弧度,而非角度,<br/>你需要把角度转化为弧度.<br/><br/>弧度=角度*Pi/180;<br/><br/>例子1：<br/>比如对边和邻边分别为a,b<br/>设角度为x，则<br/>x=atan(a/b);<br/>其中x为弧度制<br/>如需转换为角度值，则x*180/3.1415<br/><br/><br/>例子2：<br/><div class="code"><br/>//计算旋转角度&nbsp;&nbsp; &#123;弧度=角度*Pi/180&#125; &#123;两点间距离公式 根号下（&#124;X1-X2&#124;的平方+&#124;Y1-Y2&#124;的平方）&#125;<br/>&nbsp;&nbsp; double angle_tanValue=sqrt(pow(point.x-point.x,2)+pow(point.y-Right_Top_Point.y,2))/<br/>&nbsp;&nbsp;&nbsp;&nbsp;sqrt(pow(Turn_Point_L&#91;1&#93;.x-point.x,2)+pow(Turn_Point_L&#91;1&#93;.y-Right_Top_Point.y,2));<br/>&nbsp;&nbsp; //求出tan 与 sin 的弧度<br/>&nbsp;&nbsp; double angle_atanValue=atan(angle_tanValue);<br/>&nbsp;&nbsp; double angle_sinValue=sin(angle_atanValue);<br/>&nbsp;&nbsp; //弧度转换成角度<br/>&nbsp;&nbsp; double angle=angle_atanValue*180/3.1415;<br/>&nbsp;&nbsp; //取绝对值<br/>&nbsp;&nbsp; angle=fabs(angle);<br/></div>
]]>
</description>
</item><item>
<link>//gm.angeldm.com/post/213/</link>
<title><![CDATA[如何获取char* 指针的地址]]></title> 
<author>果面 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[程序开发]]></category>
<pubDate>Thu, 24 Nov 2011 13:15:07 +0000</pubDate> 
<guid>//gm.angeldm.com/post/213/</guid> 
<description>
<![CDATA[ 
	输出char*指针得到的只是它指向的内容<br/><div class="code"><br/>void getAddr()<br/>&#123;<br/>&nbsp;&nbsp;char a&#91;8&#93; = &#123;&#039;1&#039;,&#039;2&#039;,&#039;3&#039;,&#039;4&#039;,&#039;5&#039;,&#039;6&#039;,&#039;7&#039;,&#039;8&#039;&#125;;<br/>&nbsp;&nbsp;char *cp = a; <br/>&nbsp;&nbsp;cout&lt;&lt;a&lt;&lt;endl;<br/>&nbsp;&nbsp;cout&lt;&lt;&amp;a&#91;0&#93;&lt;&lt;endl;<br/>&nbsp;&nbsp;cout&lt;&lt;&amp;(*cp)&lt;&lt;endl;<br/>&nbsp;&nbsp; <br/>&nbsp;&nbsp;//下面两句输出&#91;separator&#93;的应该不是a和cp的地址吧？<br/>&nbsp;&nbsp;cout&lt;&lt;&amp;cp&lt;&lt;endl;<br/>&nbsp;&nbsp;cout&lt;&lt;&amp;a&lt;&lt;endl;<br/><br/>&#125;<br/>compiler: C++<br/>result:<br/>12345678x &quot;&nbsp;&nbsp;<br/>12345678x &quot;<br/>12345678x &quot;<br/>0x22ff4c<br/>0x22ff50<br/></div><br/>=================================================<br/>cp里面存的就是a[8]的地址，<br/>a也是a[8]的地址，<br/>用：&a[0]也没有错；<br/><br/>而cp本来就是一个指针，它指向一个变量的地址，&cp就是取的cp这个指针变量的地址了；<br/>a就是字符数组的地址，&a就是字符数组地址的地址；<br/>==================================================<br/><div class="code">cout &lt;&lt; (void *)cp;</div><br/><br/>==================================================<br/><br/><div class="code">cout &lt; &lt;&amp;cp &lt; &lt;endl; cp这个变量的地址<br/><br/>cout &lt; &lt;&amp;a &lt; &lt;endl; a数组这个对象的地址</div><br/><br/>===================================================<br/>需要采用cout<<(void*)cp<<才能得到cp的地址<br/><div class="code">&nbsp;&nbsp;char a&#91;8&#93; = &quot;Hello&quot;;<br/>&nbsp;&nbsp;char *cp = a;<br/><br/>&nbsp;&nbsp;cout&lt;&lt;a&lt;&lt;endl;<br/>&nbsp;&nbsp;cout&lt;&lt;&amp;a&#91;0&#93;&lt;&lt;endl; //和上句一样<br/>&nbsp;&nbsp;cout&lt;&lt;(void*)cp&lt;&lt;endl; //char*比较特殊，需要这样才能输出<br/>&nbsp;&nbsp;cout&lt;&lt;*cp&lt;&lt;endl;&nbsp;&nbsp;<br/>&nbsp;&nbsp;cout&lt;&lt;&amp;a&lt;&lt;endl; //这个就是数组a的实际地址了 = (void*)cp同<br/>&nbsp;&nbsp;cout&lt;&lt;&amp;cp&lt;&lt;endl; //这个应该是地址的地址，因为cp本身已经是一个指针<br/><br/>result:<br/>Hello<br/>Hello<br/>0x22ff50<br/>H<br/>0x22ff50<br/>0x22ff4c</div><br/><br/>====================================================<br/>只要在打印的时候把cp的类型强制转换为非char*类型就行了，并不是一定要void*的，例如：<br/><br/><div class="code">cout &lt; &lt;(int*)cp &lt; &lt;endl; 或cout &lt; &lt;(float*)cp &lt; &lt;endl;</div><br/>都可以。只有在char*类型下才会打印cp指向的字符串内容。<br/><br/>&a不是字符数组地址的地址，&a的意义是对数组a这个对象取地址，类型是int(*)[8]，a这个标识符本身不是个变量，更不是对象，是没有地址的。<br/><br/>
]]>
</description>
</item><item>
<link>//gm.angeldm.com/post/212/</link>
<title><![CDATA[什么叫句柄]]></title> 
<author>果面 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[程序开发]]></category>
<pubDate>Mon, 21 Nov 2011 05:18:56 +0000</pubDate> 
<guid>//gm.angeldm.com/post/212/</guid> 
<description>
<![CDATA[ 
	关于句柄<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;句柄实际上是一种指向某种资源的指针，但与指针又有所不同。<br/><br/>　　“句柄”（handle），handle的本意是把柄，把手的意思。是你与操作系统打交道的东东。举个通俗的例子，比如你考上了大学，入学后，学校（操作系统）会给你一个学生证号。注意，这个号码是学校指定的，你无法自选。有了这个号码（学生证，假设一证多用）就可以享受学校提供的服务：如你就可以去图书馆借书，去食堂吃饭，去教室上课等等。但你不能到食堂里买啤酒，因为学校不允许这种服务。而在计算机中系统提供的服务就是API调用，你有了HANDLE，就可以理直气壮地向系统提出调用API的服务。而指针的权力就大多了，有了指针你可以到处去喝酒，打架，学校（操作系统）管不着，所以句柄和指针的区别在于句柄只能调用系统提供的服务。而句柄虽然是一个能相互区别的号码，但与我们普通的ID号又有区别，普通的ID号是可以由程序员自己定义的，而句柄不行，它是对象生成时系统指定的，是为了区别系统中存在的各个对象，这个句柄不是由程序员符给的。实际应用中，最常用的就是文件句柄和窗口句柄。例如，窗口句柄的值是一个长整数，每个窗体都用一个句柄来表示。所以句柄是不会重复的，很多的函数都会用到窗体的句柄。
]]>
</description>
</item><item>
<link>//gm.angeldm.com/post/211/</link>
<title><![CDATA[float浮点不能精确表示二进制的问题]]></title> 
<author>果面 &lt;admin@yourname.com&gt;</author>
<category><![CDATA[程序开发]]></category>
<pubDate>Fri, 18 Nov 2011 07:48:12 +0000</pubDate> 
<guid>//gm.angeldm.com/post/211/</guid> 
<description>
<![CDATA[ 
	int和float都是4字节32位表示形式。为什么float的范围大于int？<br/><br/>float精度为6～7位。1.66*10^10的数字结果并不是166 0000 0000 指数越大，误差越大。<br/><br/>这些问题，都是浮点数的存储方式造成的。&nbsp;&nbsp;<br/><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;float和double在存储方式上都是遵从IEEE的规范的，float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;无论是单精度还是双精度在存储中都分为三个部分：<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;符号位(Sign) : 0代表正，1代表为负<br/>&nbsp;&nbsp;&nbsp;&nbsp;指数位（Exponent）:用于存储科学计数法中的指数数据，并且采用移位存储<br/>&nbsp;&nbsp;&nbsp;&nbsp;尾数部分（Mantissa）：尾数部分<br/><br/>其中float的存储方式如下图所示：<br/><a href="//gm.angeldm.com/attachment.php?fid=11" target="_blank"><img src="//gm.angeldm.com/attachment.php?fid=11" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>float类型的存储方式<br/><br/>而双精度的存储方式为:<br/><a href="//gm.angeldm.com/attachment.php?fid=12" target="_blank"><img src="//gm.angeldm.com/attachment.php?fid=12" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>double类型数据的存储方式<br/><br/><div class="quote"><div class="quote-title">引用</div><div class="quote-content">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 将一个float型转化为内存存储格式的步骤为：<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp; （1）先将这个实数的绝对值化为二进制格式。 <br/>&nbsp;&nbsp;&nbsp;&nbsp; （2）将这个二进制格式实数的小数点左移或右移n位，直到小数点移动到第一个有效数字的右边。 <br/>&nbsp;&nbsp;&nbsp;&nbsp; （3）从小数点右边第一位开始数出二十三位数字放入第22到第0位。 <br/>&nbsp;&nbsp;&nbsp;&nbsp; （4）如果实数是正的，则在第31位放入“0”，否则放入“1”。 <br/>&nbsp;&nbsp;&nbsp;&nbsp; （5）如果n 是左移得到的，说明指数是正的，第30位放入“1”。如果n是右移得到的或n=0，则第30位放入“0”。 <br/>&nbsp;&nbsp;&nbsp;&nbsp; （6）如果n是左移得到的，则将n减去1后化为二进制，并在左边加“0”补足七位，放入第29到第23位。如果n是右移得到的或n=0，则将n化为二进制后在左边加“0”补足七位，再各位求反，再放入第29到第23位。</div></div><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp; R32.24和R64.53的存储方式都是用科学计数法来存储数据的，比如8.25用十进制的科学计数法表示就为:8.25*clip_image0021,而120.5可以表示为:1.205*clip_image0022,计算机根本不认识十进制的数据，他只认识0，1，所以在计算机存储中，首先要将上面的数更改为二进制的科学计数法表示，8.25用二进制表示可表示为1000.01,120.5用二进制表示为：1110110.1用二进制的科学计数法表示1000.01可以表示为1.0001*clip_image002[2],1110110.1可以表示为1.1101101*clip_image002[3],任何一个数都的科学计数法表示都为1.xxx*clip_image002[1],尾数部分就可以表示为xxxx,第一位都是1嘛，干嘛还要表示呀？可以将小数点前面的1省略，所以23bit的尾数部分，可以表示的精度却变成了24bit，道理就是在这里，那24bit能精确到小数点后几位呢，我们知道9的二进制表示为1001，所以4bit能精确十进制中的1位小数点，24bit就能使float能精确到小数点后6位，而对于指数部分，因为指数可正可负，8位的指数位能表示的指数范围就应该为:-127-128了，所以指数部分的存储采用移位存储，存储的数据为元数据+127，下面就看看8.25和120.5在内存中真正的存储方式。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp; 首先看下8.25，用二进制的科学计数法表示为:1.0001*clip_image002[2]<br/><br/>按照上面的存储方式，符号位为:0，表示为正，指数位为:3+127=130 ,位数部分为,故8.25的存储方式如下图所示:<br/><a href="//gm.angeldm.com/attachment.php?fid=13" target="_blank"><img src="//gm.angeldm.com/attachment.php?fid=13" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>单精度浮点数8.25的存储方式<br/><br/>而单精度浮点数120.5的存储方式如下图所示:<br/><a href="//gm.angeldm.com/attachment.php?fid=19" target="_blank"><img src="//gm.angeldm.com/attachment.php?fid=19" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>单精度数120.5的存储方式<br/><br/><br/><div class="quote"><div class="quote-title">引用</div><div class="quote-content">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将一个内存存储的float二进制格式转化为十进制的步骤： <br/>&nbsp;&nbsp;&nbsp;&nbsp; （1）将第22位到第0位的二进制数写出来，在最左边补一位“1”，得到二十四位有效数字。将小数点点在最左边那个“1”的右边。 <br/>&nbsp;&nbsp;&nbsp;&nbsp; （2）取出第29到第23位所表示的值n。当30位是“0”时将n各位求反。当30位是“1”时将n增1。 <br/>&nbsp;&nbsp;&nbsp;&nbsp; （3）将小数点左移n位（当30位是“0”时）或右移n位（当30位是“1”时），得到一个二进制表示的实数。 <br/>&nbsp;&nbsp;&nbsp;&nbsp; （4）将这个二进制实数化为十进制，并根据第31位是“0”还是“1”加上正号或负号即可。</div></div><br/><br/>那么如果给出内存中一段数据，并且告诉你是单精度存储的话，你如何知道该数据的十进制数值呢？其实就是对上面的反推过程，比如给出如下内存数据：0100001011101101000000000000，首先我们现将该数据分段，0 10000 0101 110 1101 0000 0000 0000 0000，在内存中的存储就为下图所示：<br/><a href="//gm.angeldm.com/attachment.php?fid=15" target="_blank"><img src="//gm.angeldm.com/attachment.php?fid=15" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>浮点数的存储方式 float(转 - yanpol - yanpol的博客<br/><br/>根据我们的计算方式，可以计算出，这样一组数据表示为:1.1101101*clip_image002[3]=120.5<br/><br/>而双精度浮点数的存储和单精度的存储大同小异，不同的是指数部分和尾数部分的位数。所以这里不再详细的介绍双精度的存储方式了，只将120.5的最后存储方式图给出，大家可以仔细想想为何是这样子的<br/><a href="//gm.angeldm.com/attachment.php?fid=20" target="_blank"><img src="//gm.angeldm.com/attachment.php?fid=20" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>文本框: 0 100 0000 0101 1101 1010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000<br/><br/>下面我就这个基础知识点来解决一个我们的一个疑惑，请看下面一段程序，注意观察输出结果<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float f = 2.2f;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;double d = (double)f;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(d.ToString("0.0000000000000"));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f = 2.25f;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;d = (double)f;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(d.ToString("0.0000000000000"));<br/><br/>可能输出的结果让大家疑惑不解，单精度的2.2转换为双精度后，精确到小数点后13位后变为了2.2000000476837，而单精度的2.25转换为双精度后，变为了2.2500000000000，为何2.2在转换后的数值更改了而2.25却没有更改呢？很奇怪吧？其实通过上面关于两种存储结果的介绍，我们已经大概能找到答案。首先我们看看2.25的单精度存储方式，很简单 0 1000 0001 001 0000 0000 0000 0000 0000,而2.25的双精度表示为:0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,这样2.25在进行强制转换的时候，数值是不会变的，而我们再看看2.2呢，2.2用科学计数法表示应该为：将十进制的小数转换为二进制的小数的方法为将小数*2，取整数部分，所以0.282=0.4，所以二进制小数第一位为0.4的整数部分0，0.4×2=0.8，第二位为0,0.8*2=1.6,第三位为1，0.6×2 = 1.2，第四位为1，0.2*2=0.4，第五位为0，这样永远也不可能乘到=1.0，得到的二进制是一个无限循环的排列 00110011001100110011... ,对于单精度数据来说，尾数只能表示24bit的精度，所以2.2的float存储为:<br/><a href="//gm.angeldm.com/attachment.php?fid=17" target="_blank"><img src="//gm.angeldm.com/attachment.php?fid=17" class="insertimage" alt="点击在新窗口中浏览此图片" title="点击在新窗口中浏览此图片" border="0"/></a><br/>单精度数202的存储方式<br/><br/>但是这样存储方式，换算成十进制的值，却不会是2.2的，应为十进制在转换为二进制的时候可能会不准确，如2.2，而double类型的数据也存在同样的问题，所以在浮点数表示中会产生些许的误差，在单精度转换为双精度的时候，也会存在误差的问题，对于能够用二进制表示的十进制数据，如2.25，这个误差就会不存在，所以会出现上面比较奇怪的输出结果。<br/><br/><br/>附注：<br/><br/><div class="quote"><div class="quote-title">引用</div><div class="quote-content">小数的二进制表示问题<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 首先我们要搞清楚下面两个问题：<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp; (1)&nbsp;&nbsp;十进制整数如何转化为二进制数<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 算法很简单。举个例子，11表示成二进制数：<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11/2=5&nbsp;&nbsp; 余&nbsp;&nbsp; 1<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5/2=2&nbsp;&nbsp; 余&nbsp;&nbsp; 1<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2/2=1&nbsp;&nbsp; 余&nbsp;&nbsp; 0<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1/2=0&nbsp;&nbsp; 余&nbsp;&nbsp; 1<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0结束&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11二进制表示为(从下往上):1011<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这里提一点：只要遇到除以后的结果为0了就结束了，大家想一想，所有的整数除以2是不是一定能够最终得到0。换句话说，所有的整数转变为二进制数的算法会不会无限循环下去呢？绝对不会，整数永远可以用二进制精确表示 ，但小数就不一定了。<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(2) 十进制小数如何转化为二进制数<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 算法是乘以2直到没有了小数为止。举个例子，0.9表示成二进制数<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.9*2=1.8&nbsp;&nbsp; 取整数部分&nbsp;&nbsp;1<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.8(1.8的小数部分)*2=1.6&nbsp;&nbsp;&nbsp;&nbsp;取整数部分&nbsp;&nbsp;1<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.6*2=1.2&nbsp;&nbsp; 取整数部分&nbsp;&nbsp;1<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.2*2=0.4&nbsp;&nbsp; 取整数部分&nbsp;&nbsp;0<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.4*2=0.8&nbsp;&nbsp; 取整数部分&nbsp;&nbsp;0<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.8*2=1.6&nbsp;&nbsp; 取整数部分&nbsp;&nbsp;1<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.6*2=1.2&nbsp;&nbsp; 取整数部分&nbsp;&nbsp;0<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.........&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.9二进制表示为(从上往下): 1100100100100......<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意：上面的计算过程循环了，也就是说*2永远不可能消灭小数部分，这样算法将无限下去。很显然，小数的二进制表示有时是不可能精确的 。其实道理很简单，十进制系统中能不能准确表示出1/3呢？同样二进制系统也无法准确表示1/10。这也就解释了为什么浮点型减法出现了"减不尽"的精度丢失问题。<br/></div></div>
]]>
</description>
</item>
</channel>
</rss>