添加消息響應(yīng)WM_CTLCOLOR,
Static代碼如下:
HBRUSH CTest1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: Change any attributes of the DC here
CFont m_font; //聲明變量
m_font.CreatePointFont(600,"華文行楷"); //設(shè)置字體大小和類型
if(pWnd->GetDlgCtrlID()==IDC_STATIC01)//可以用CTLCOLOR_STATIC表示靜態(tài)控件
{
pDC->SelectObject(&m_font); //設(shè)置字體
pDC->SetTextColor(RGB(0,0,255)); //設(shè)置字體顏色
pDC->SetBkMode(TRANSPARENT); //屬性設(shè)置為透明
return (HBRUSH)::GetStockObject(NULL_BRUSH); //不返回畫刷
}
// TODO: Return a different brush if the default is not desired
return hbr;
}
Radio和Check代碼如下
HBRUSH CLoginDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
//HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
HBRUSH hbr = ::CreateSolidBrush(#f9f9f9);
// TODO: Change any attributes of the DC here
if (pWnd->GetDlgCtrlID() == IDC_RADIO_REALNAME ||
pWnd->GetDlgCtrlID() == IDC_RADIO_ANONYMOUS ||
pWnd->GetDlgCtrlID() == IDC_CHECK_SELFSELECT)
{
pDC->SetBkMode(TRANSPARENT);
CRect rc;
pWnd->GetWindowRect(&rc);
ScreenToClient(&rc);
CDC* dc = GetDC();
pDC->BitBlt(0,0,rc.Width(),rc.Height(),dc,rc.left,rc.top,SRCCOPY); //把父窗口背景先畫到按鈕上
ReleaseDC(dc);
hbr = (HBRUSH) ::GetStockObject(NULL_BRUSH);
}
}
采用TCP連接的C/S模式軟件,連接的雙方在連接空閑狀態(tài)時(shí),如果任意一方意外崩潰、當(dāng)機(jī)、網(wǎng)線斷開或路由器故障,另一方無(wú)法得知TCP連接已經(jīng)失效,除非繼續(xù)在此連接上發(fā)送數(shù)據(jù)導(dǎo)致錯(cuò)誤返回。很多時(shí)候,這不是我們需要的。我們希望服務(wù)器端和客戶端都能及時(shí)有效地檢測(cè)到連接失效,然后優(yōu)雅地完成一些清理工作并把錯(cuò)誤報(bào)告給用戶。
如何及時(shí)有效地檢測(cè)到一方的非正常斷開,一直有兩種技術(shù)可以運(yùn)用。一種是由TCP協(xié)議層實(shí)現(xiàn)的Keepalive,另一種是由應(yīng)用層自己實(shí)現(xiàn)的心跳包。
TCP默認(rèn)并不開啟Keepalive功能,因?yàn)殚_啟Keepalive功能需要消耗額外的寬帶和流量,盡管這微不足道,但在按流量計(jì)費(fèi)的環(huán)境下增加了費(fèi)用,另一方面,Keepalive設(shè)置不合理時(shí)可能會(huì)因?yàn)槎虝旱木W(wǎng)絡(luò)波動(dòng)而斷開健康的TCP連接。并且,默認(rèn)的Keepalive超時(shí)需要7,200,000 milliseconds,即2小時(shí),探測(cè)次數(shù)為5次。
對(duì)于Win2K/XP/2003,可以從下面的注冊(cè)表項(xiàng)找到影響整個(gè)系統(tǒng)所有連接的keepalive參數(shù):
[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters]
"KeepAliveTime”=dword:006ddd00
"KeepAliveInterval"=dword:000003e8
"MaxDataRetries"="5"
對(duì)于實(shí)用的程序來(lái)說(shuō),2小時(shí)的空閑時(shí)間太長(zhǎng)。因此,我們需要手工開啟Keepalive功能并設(shè)置合理的Keepalive參數(shù)。
// 開啟KeepAlive
BOOL bKeepAlive = TRUE;
int nRet = ::setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));

if (nRet == SOCKET_ERROR)
{
return FALSE;
}

// 設(shè)置KeepAlive參數(shù)
tcp_keepalive alive_in = {0};
tcp_keepalive alive_out = {0};
alive_in.keepalivetime = 5000; // 開始首次KeepAlive探測(cè)前的TCP空閉時(shí)間

alive_in.keepaliveinterval = 1000; // 兩次KeepAlive探測(cè)間的時(shí)間間隔

alive_in.onoff = TRUE;
unsigned long ulBytesReturn = 0;

nRet = WSAIoctl(socket_handle, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in),
&alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);
if (nRet == SOCKET_ERROR)
{
return FALSE;


}


開啟Keepalive選項(xiàng)之后,對(duì)于使用IOCP模型的服務(wù)器端程序來(lái)說(shuō),一旦檢測(cè)到連接斷開,GetQueuedCompletionStatus函數(shù)將立即返回FALSE,使得服務(wù)器端能及時(shí)清除該連接、釋放該連接相關(guān)的資源。對(duì)于使用select模型的客戶端來(lái)說(shuō),連接斷開被探測(cè)到時(shí),以recv目的阻塞在socket上的select方法將立即返回SOCKET_ERROR,從而得知連接已失效,客戶端程序便有機(jī)會(huì)及時(shí)執(zhí)行清除工作、提醒用戶或重新連接。
另一種技術(shù),由應(yīng)用程序自己發(fā)送心跳包來(lái)檢測(cè)連接的健康性。客戶端可以在一個(gè)Timer中或低級(jí)別的線程中定時(shí)向發(fā)服務(wù)器發(fā)送一個(gè)短小精悍的包,并等待服務(wù)器的回應(yīng)。客戶端程序在一定時(shí)間內(nèi)沒有收到服務(wù)器回應(yīng)即認(rèn)為連接不可用,同樣,服務(wù)器在一定時(shí)間內(nèi)沒有收到客戶端的心跳包則認(rèn)為客戶端已經(jīng)掉線。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
windows下此處的”非正常斷開”指TCP連接不是以優(yōu)雅的方式斷開,如網(wǎng)線故障等物理鏈路的原因,還有突然主機(jī)斷電等原因.
有兩種方法可以檢測(cè):
1.TCP連接雙方定時(shí)發(fā)握手消息
2.利用TCP協(xié)議棧中的KeepAlive探測(cè)
第二種方法簡(jiǎn)單可靠,只需對(duì)TCP連接兩個(gè)Socket設(shè)定KeepAlive探測(cè),
所以本文只講第二種方法在Linux,Window2000下的實(shí)現(xiàn)(在其它的平臺(tái)上沒有作進(jìn)一步的測(cè)試)

Windows 2000平臺(tái)下 頭文件
#include <mstcpip.h>
//定義結(jié)構(gòu)及宏
/*
struct TCP_KEEPALIVE {
u_longonoff;
u_longkeepalivetime;
u_longkeepaliveinterval;
} ;
*/

tcp_keepalive live,liveout;
live.keepaliveinterval=5000; //每5秒發(fā)一次探測(cè)報(bào)文,發(fā)5次沒有回應(yīng),就斷開
live.keepalivetime=30000;//超過(guò)30s沒有數(shù)據(jù),就發(fā)送控測(cè)包
live.onoff=TRUE;
int Opt = 1;
int iRet = setsockopt(Accept,SOL_SOCKET,SO_KEEPALIVE,(char *)&Opt,sizeof(int));
if(iRet == 0)
{
DWORD dw;
if(::WSAIoctl(Accept,SIO_KEEPALIVE_VALS,
&live,sizeof(live),&liveout,sizeof(liveout),
&dw,NULL,NULL)== SOCKET_ERROR){
}
}
ACE下代碼 //by rainfish blog.csdn.net/bat603
int Opt = 1;
//在測(cè)試過(guò)程中,發(fā)現(xiàn)檢測(cè)的次數(shù)是5次,即下面的設(shè)置中,從最近一次消息開始計(jì)算的10秒后,每次間隔5秒,連續(xù)發(fā)送5次,即35秒發(fā)現(xiàn)網(wǎng)絡(luò)斷了
tcp_keepalive live,liveout;
live.keepaliveinterval=5000; //每次檢測(cè)的間隔 (單位毫秒)
live.keepalivetime=10000; //第一次開始發(fā)送的時(shí)間(單位毫秒)
live.onoff=TRUE;
int iRet = stream.set_option(SOL_SOCKET,SO_KEEPALIVE,&Opt,sizeof(int));
if(iRet == 0)
{
DWORD dw;
//此處顯示了在ACE下獲取套接字的方法,即句柄的(SOCKET)化就是句柄
if(WSAIoctl((SOCKET)h,SIO_KEEPALIVE_VALS,&live,sizeof(live),
&liveout,sizeof(liveout),&dw,NULL,NULL)== SOCKET_ERROR)
{
//Delete Client
return;
}
}
Linux平臺(tái)下
#include "/usr/include/linux/tcp.h"
#include "/usr/include/linux/socket.h"
////KeepAlive實(shí)現(xiàn),單位秒
//下面代碼要求有ACE,如果沒有包含ACE,則請(qǐng)把用到的ACE函數(shù)改成linux相應(yīng)的接口
int keepAlive = 1;//設(shè)定KeepAlive
int keepIdle = 5;//開始首次KeepAlive探測(cè)前的TCP空閉時(shí)間
int keepInterval = 5;//兩次KeepAlive探測(cè)間的時(shí)間間隔
int keepCount = 3;//判定斷開前的KeepAlive探測(cè)次數(shù)
if(setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1)
{
ACE_DEBUG ((LM_INFO,
ACE_TEXT ("(%P|%t) setsockopt SO_KEEPALIVE error!/n")));
}
if(setsockopt(s,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle)) == -1)
{
ACE_DEBUG ((LM_INFO,
ACE_TEXT ("(%P|%t) setsockopt TCP_KEEPIDLE error!/n")));
}
if(setsockopt(s,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval)) == -1)
{
ACE_DEBUG ((LM_INFO,
ACE_TEXT ("(%P|%t) setsockopt TCP_KEEPINTVL error!/n")));
}
if(setsockopt(s,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount)) == -1)
{
ACE_DEBUG ((LM_INFO,
ACE_TEXT ("(%P|%t)setsockopt TCP_KEEPCNT error!/n")));
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
本文轉(zhuǎn)自:
http://www.shnenglu.com/API/archive/2013/08/13/202516.html
API:要取得屏幕大小,可以用下面幾個(gè)函數(shù):
int cx = GetSystemMetrics( SM_CXFULLSCREEN );
int cy = GetSystemMetrics( SM_CYFULLSCREEN );
通過(guò)上邊兩個(gè)函數(shù)獲取的是 顯示屏幕的大小,但不包括任務(wù)欄等區(qū)域。
int cx = GetSystemMetrics( SM_CXSCREEN );
int cy = GetSystemMetrics( SM_CYSCREEN );
這兩個(gè)函數(shù)獲取的是真正屏幕的大小。
MFC:
HDC hDC = ::GetDC(HWND(NULL)); // 得到屏幕DC
int x = ::GetDeviceCaps(hDC,HORZRES); // 寬
int y = ::GetDeviceCaps(hDC,VERTRES); // 高
::ReleaseDC(HWND(NULL),hDC); // 釋放DC
MFC程序vs2008編譯通過(guò),運(yùn)行時(shí)出錯(cuò),無(wú)法打開,提示f:\dd\xxxx的docsingl.cpp中的210行,找到以下代碼:void CSingleDocTemplate::SetDefaultTitle(CDocument* pDocument)
{
CString strDocName;
if (!GetDocString(strDocName, CDocTemplate::docName) ||
strDocName.IsEmpty())
{
// use generic 'untitled'
ENSURE(strDocName.LoadString(AFX_IDS_UNTITLED));
}
pDocument->SetTitle(strDocName);
}
紅色行就是出錯(cuò)地方,原因是資源文件引起,一般是從英文或其它語(yǔ)言改成簡(jiǎn)體中文而造成,把語(yǔ)言相關(guān)改為以下幾行#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
#ifdef _WIN32
LANGUAGE 4, 2
#pragma code_page(936)
#endif //_WIN32
Linux下的段錯(cuò)誤產(chǎn)生的原因及調(diào)試方法 原文地址:http://www.upsdn.net/html/2006-11/775.html
參考地址:http://www.cnblogs.com/khler/archive/2010/09/16/1828349.html
簡(jiǎn)而言之,產(chǎn)生段錯(cuò)誤就是訪問了錯(cuò)誤的內(nèi)存段,一般是你沒有權(quán)限,或者根本就不存在對(duì)應(yīng)的物理內(nèi)存,尤其常見的是訪問0地址.
一般來(lái)說(shuō),段錯(cuò)誤就是指訪問的內(nèi)存超出了系統(tǒng)所給這個(gè)程序的內(nèi)存空間,通常這個(gè)值是由gdtr來(lái)保存的,他是一個(gè)48位的寄存器,其中的32位是保存由它指向的gdt表,后13位保存相應(yīng)于gdt的下標(biāo),最后3位包括了程序是否在內(nèi)存中以及程序的在cpu中的運(yùn)行級(jí)別,指向的gdt是由以64位為一個(gè)單位的表,在這張表中就保存著程序運(yùn)行的代碼段以及數(shù)據(jù)段的起始地址以及與此相應(yīng)的段限和頁(yè)面交換還有程序運(yùn)行級(jí)別還有內(nèi)存粒度等等的信息。一旦一個(gè)程序發(fā)生了越界訪問,cpu就會(huì)產(chǎn)生相應(yīng)的異常保護(hù),于是segmentation fault就出現(xiàn)了.
在編程中以下幾類做法容易導(dǎo)致段錯(cuò)誤,基本是是錯(cuò)誤地使用指針引起的
1)訪問系統(tǒng)數(shù)據(jù)區(qū),尤其是往 系統(tǒng)保護(hù)的內(nèi)存地址寫數(shù)據(jù)
最常見就是給一個(gè)指針以0地址
2)內(nèi)存越界(數(shù)組越界,變量類型不一致等) 訪問到不屬于你的內(nèi)存區(qū)域
解決方法
我們?cè)谟肅/C++語(yǔ)言寫程序的時(shí)侯,內(nèi)存管理的絕大部分工作都是需要我們來(lái)做的。實(shí)際上,內(nèi)存管理是一個(gè)比較繁瑣的工作,無(wú)論你多高明,經(jīng)驗(yàn)多豐富,難 免會(huì)在此處犯些小錯(cuò)誤,而通常這些錯(cuò)誤又是那么的淺顯而易于消除。但是手工“除蟲”(debug),往往是效率低下且讓人厭煩的,本文將就"段錯(cuò)誤"這個(gè) 內(nèi)存訪問越界的錯(cuò)誤談?wù)勅绾慰焖俣ㄎ贿@些"段錯(cuò)誤"的語(yǔ)句。
下面將就以下的一個(gè)存在段錯(cuò)誤的程序介紹幾種調(diào)試方法:
1 dummy_function (void) 2 { 3 unsigned char *ptr = 0x00; 4 *ptr = 0x00; 5 } 6 7 int main (void) 8 { 9 dummy_function (); 10 11 return 0; 12 }
|
作為一個(gè)熟練的C/C++程序員,以上代碼的bug應(yīng)該是很清楚的,因?yàn)樗鼑L試操作地址為0的內(nèi)存區(qū)域,而這個(gè)內(nèi)存區(qū)域通常是不可訪問的禁區(qū),當(dāng)然就會(huì)出錯(cuò)了。我們嘗試編譯運(yùn)行它:
xiaosuo@gentux test $ ./a.out 段錯(cuò)誤
|
果然不出所料,它出錯(cuò)并退出了。
1.利用gdb逐步查找段錯(cuò)誤:這種方法也是被大眾所熟知并廣泛采用的方法,首先我們需要一個(gè)帶有調(diào)試信息的可執(zhí)行程序,所以我們加上“-g -rdynamic"的參數(shù)進(jìn)行編譯,然后用gdb調(diào)試運(yùn)行這個(gè)新編譯的程序,具體步驟如下:
xiaosuo@gentux test $ gcc -g -rdynamic d.c xiaosuo@gentux test $ gdb ./a.out GNU gdb 6.5 Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) r Starting program: /home/xiaosuo/test/a.out
Program received signal SIGSEGV, Segmentation fault. 0x08048524 in dummy_function () at d.c:4 4 *ptr = 0x00; (gdb)
|
哦?!好像不用一步步調(diào)試我們就找到了出錯(cuò)位置d.c文件的第4行,其實(shí)就是如此的簡(jiǎn)單。
從這里我們還發(fā)現(xiàn)進(jìn)程是由于收到了SIGSEGV信號(hào)而結(jié)束的。通過(guò)進(jìn)一步的查閱文檔(man 7 signal),我們知道SIGSEGV默認(rèn)handler的動(dòng)作是打印”段錯(cuò)誤"的出錯(cuò)信息,并產(chǎn)生Core文件,由此我們又產(chǎn)生了方法二。
2.分析Core文件:Core文件是什么呢?
The default action of certain signals is to cause a process to terminate and produce a core dump file, a disk file containing an image of the process's memory at the time of termination. A list of the signals which cause a process to dump core can be found in signal(7). |
以 上資料摘自man page(man 5 core)。不過(guò)奇怪了,我的系統(tǒng)上并沒有找到core文件。后來(lái),憶起為了漸少系統(tǒng)上的拉圾文件的數(shù)量(本人有些潔癖,這也是我喜歡Gentoo的原因 之一),禁止了core文件的生成,查看了以下果真如此,將系統(tǒng)的core文件的大小限制在512K大小,再試:
xiaosuo@gentux test $ ulimit -c 0 xiaosuo@gentux test $ ulimit -c 1000 xiaosuo@gentux test $ ulimit -c 1000 xiaosuo@gentux test $ ./a.out 段錯(cuò)誤 (core dumped) xiaosuo@gentux test $ ls a.out core d.c f.c g.c pango.c test_iconv.c test_regex.c
|
core文件終于產(chǎn)生了,用gdb調(diào)試一下看看吧:
xiaosuo@gentux test $ gdb ./a.out core GNU gdb 6.5 Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
warning: Can't read pathname for load map: 輸入/輸出錯(cuò)誤. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 Core was generated by `./a.out'. Program terminated with signal 11, Segmentation fault. #0 0x08048524 in dummy_function () at d.c:4 4 *ptr = 0x00;
|
哇,好歷害,還是一步就定位到了錯(cuò)誤所在地,佩服一下Linux/Unix系統(tǒng)的此類設(shè)計(jì)。
接著考慮下去,以前用windows系統(tǒng)下的ie的時(shí)侯,有時(shí)打開某些網(wǎng)頁(yè),會(huì)出現(xiàn)“運(yùn)行時(shí)錯(cuò)誤”,這個(gè)時(shí)侯如果恰好你的機(jī)器上又裝有windows的編譯器的話,他會(huì)彈出來(lái)一個(gè)對(duì)話框,問你是否進(jìn)行調(diào)試,如果你選擇是,編譯器將被打開,并進(jìn)入調(diào)試狀態(tài),開始調(diào)試。
Linux下如何做到這些呢?我的大腦飛速地旋轉(zhuǎn)著,有了,讓它在SIGSEGV的handler中調(diào)用gdb,于是第三個(gè)方法又誕生了:
3.段錯(cuò)誤時(shí)啟動(dòng)調(diào)試:#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h>
void dump(int signo) { char buf[1024]; char cmd[1024]; FILE *fh;
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid()); if(!(fh = fopen(buf, "r"))) exit(0); if(!fgets(buf, sizeof(buf), fh)) exit(0); fclose(fh); if(buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; snprintf(cmd, sizeof(cmd), "gdb %s %d", buf, getpid()); system(cmd);
exit(0); }
void dummy_function (void) { unsigned char *ptr = 0x00; *ptr = 0x00; }
int main (void) { signal(SIGSEGV, &dump); dummy_function ();
return 0; }
|
編譯運(yùn)行效果如下:
xiaosuo@gentux test $ gcc -g -rdynamic f.c xiaosuo@gentux test $ ./a.out GNU gdb 6.5 Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
Attaching to program: /home/xiaosuo/test/a.out, process 9563 Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 0xffffe410 in __kernel_vsyscall () (gdb) bt #0 0xffffe410 in __kernel_vsyscall () #1 0xb7ee4b53 in waitpid () from /lib/libc.so.6 #2 0xb7e925c9 in strtold_l () from /lib/libc.so.6 #3 0x08048830 in dump (signo=11) at f.c:22 #4 <signal handler called> #5 0x0804884c in dummy_function () at f.c:31 #6 0x08048886 in main () at f.c:38
|
怎么樣?是不是依舊很酷?
以上方法都是在系統(tǒng)上有g(shù)db的前提下進(jìn)行的,如果沒有呢?其實(shí)glibc為我們提供了此類能夠dump棧內(nèi)容的函數(shù)簇,詳見/usr/include/execinfo.h(這些函數(shù)都沒有提供man page,難怪我們找不到),另外你也可以通過(guò)
gnu的手冊(cè)進(jìn)行學(xué)習(xí)。
4.利用backtrace和objdump進(jìn)行分析:重寫的代碼如下:
#include <execinfo.h> #include <stdio.h> #include <stdlib.h> #include <signal.h>
/* A dummy function to make the backtrace more interesting. */ void dummy_function (void) { unsigned char *ptr = 0x00; *ptr = 0x00; }
void dump(int signo) { void *array[10]; size_t size; char **strings; size_t i;
size = backtrace (array, 10); strings = backtrace_symbols (array, size);
printf ("Obtained %zd stack frames.\n", size);
for (i = 0; i < size; i++) printf ("%s\n", strings[i]);
free (strings);
exit(0); }
int main (void) { signal(SIGSEGV, &dump); dummy_function ();
return 0; }
|
編譯運(yùn)行結(jié)果如下:
xiaosuo@gentux test $ gcc -g -rdynamic g.c xiaosuo@gentux test $ ./a.out Obtained 5 stack frames. ./a.out(dump+0x19) [0x80486c2] [0xffffe420] ./a.out(main+0x35) [0x804876f] /lib/libc.so.6(__libc_start_main+0xe6) [0xb7e02866] ./a.out [0x8048601]
|
這次你可能有些失望,似乎沒能給出足夠的信息來(lái)標(biāo)示錯(cuò)誤,不急,先看看能分析出來(lái)什么吧,用objdump反匯編程序,找到地址0x804876f對(duì)應(yīng)的代碼位置:
xiaosuo@gentux test $ objdump -d a.out
|
8048765: e8 02 fe ff ff call 804856c <signal@plt> 804876a: e8 25 ff ff ff call 8048694 <dummy_function> 804876f: b8 00 00 00 00 mov $0x0,%eax 8048774: c9 leave
|
我們還是找到了在哪個(gè)函數(shù)(dummy_function)中出錯(cuò)的,信息已然不是很完整,不過(guò)有總比沒有好的啊!
后記:本文給出了分析"段錯(cuò)誤"的幾種方法,不要認(rèn)為這是與孔乙己先生的"回"字四種寫法一樣的哦,因?yàn)槊糠N方法都有其自身的適用范圍和適用環(huán)境,請(qǐng)酌情使用,或遵醫(yī)囑。
部分資料來(lái)源于xiaosuo @ cnblog.cn, 特此致謝
作者:upsdn整理 更新日期:2006-11-03
來(lái)源:upsdn.net 瀏覽次數(shù):
其它調(diào)試辦法
在Linux上的使用開源C++日志庫(kù)---log4cplus
linux下追蹤函數(shù)調(diào)用堆棧
一般察看函數(shù)運(yùn)行時(shí)堆棧的方法是使用GDB之類的外部調(diào)試器,但是,有些時(shí)候?yàn)榱朔治龀绦虻腂UG,(主要針對(duì)長(zhǎng)時(shí)間運(yùn)行程序的分析),在程序出錯(cuò)時(shí)打印出函數(shù)的調(diào)用堆棧是非常有用的。
在頭文件"execinfo.h"中聲明了三個(gè)函數(shù)用于獲取當(dāng)前線程的函數(shù)調(diào)用堆棧
Function: int backtrace(void **buffer,int size)
該函數(shù)用與獲取當(dāng)前線程的調(diào)用堆棧,獲取的信息將會(huì)被存放在buffer中,它是一個(gè)指針列表。參數(shù) size 用來(lái)指定buffer中可以保存多少個(gè)void* 元素。函數(shù)返回值是實(shí)際獲取的指針個(gè)數(shù),最大不超過(guò)size大小
在buffer中的指針實(shí)際是從堆棧中獲取的返回地址,每一個(gè)堆棧框架有一個(gè)返回地址
注意某些編譯器的優(yōu)化選項(xiàng)對(duì)獲取正確的調(diào)用堆棧有干擾,另外內(nèi)聯(lián)函數(shù)沒有堆棧框架;刪除框架指針也會(huì)使無(wú)法正確解析堆棧內(nèi)容
Function: char ** backtrace_symbols (void *const *buffer, int size)
backtrace_symbols將從backtrace函數(shù)獲取的信息轉(zhuǎn)化為一個(gè)字符串?dāng)?shù)組. 參數(shù)buffer應(yīng)該是從backtrace函數(shù)獲取的數(shù)組指針,size是該數(shù)組中的元素個(gè)數(shù)(backtrace的返回值)
函數(shù)返回值是一個(gè)指向字符串?dāng)?shù)組的指針,它的大小同buffer相同.每個(gè)字符串包含了一個(gè)相對(duì)于buffer中對(duì)應(yīng)元素的可打印信息.它包括函數(shù)名,函數(shù)的偏移地址,和實(shí)際的返回地址
現(xiàn)在,只有使用ELF二進(jìn)制格式的程序和苦衷才能獲取函數(shù)名稱和偏移地址.在其他系統(tǒng),只有16進(jìn)制的返回地址能被獲取.另外,你可能需要傳遞相應(yīng)的標(biāo)志給鏈接器,以能支持函數(shù)名功能(比如,在使用GNU ld的系統(tǒng)中,你需要傳遞(-rdynamic))
該函數(shù)的返回值是通過(guò)malloc函數(shù)申請(qǐng)的空間,因此調(diào)用這必須使用free函數(shù)來(lái)釋放指針.
注意:如果不能為字符串獲取足夠的空間函數(shù)的返回值將會(huì)為NULL
Function:void backtrace_symbols_fd (void *const *buffer, int size, int fd)
backtrace_symbols_fd與backtrace_symbols 函數(shù)具有相同的功能,不同的是它不會(huì)給調(diào)用者返回字符串?dāng)?shù)組,而是將結(jié)果寫入文件描述符為fd的文件中,每個(gè)函數(shù)對(duì)應(yīng)一行.它不需要調(diào)用malloc函數(shù),因此適用于有可能調(diào)用該函數(shù)會(huì)失敗的情況。
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
/* Obtain a backtrace and print it to stdout. */
void print_trace (void)
{
void *array[10];
size_t size;
char **strings;
size_t i;
size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
printf ("Obtained %zd stack frames.\n", size);
for (i = 0; i < size; i++)
printf ("%s\n", strings[i]);
free (strings);
}
/* A dummy function to make the backtrace more interesting. */
void dummy_function (void)
{
print_trace ();
}
int main (void)
{
dummy_function ();
return 0;
}
編譯運(yùn)行的結(jié)果如下: # gcc bt.c -rdynamic -o bt # ./bt Obtained 5 stack frames../bt(_Z11print_tracev+0x19) [0x804870d]./bt(_Z14dummy_functionv+0xb) [0x8048779]./bt(main+0x16) [0x8048792]/lib/libc.so.6(__libc_start_main+0xdc) [0x116e9c]./bt(__gxx_personality_v0+0x31) [0x8048641]注: addr2line - convert addresses into file names and line numbers.
GetExitCodeThread函數(shù)是獲得線程的退出碼,
函數(shù):
GetExitCodeThread()
功能:獲取一個(gè)結(jié)束線程的返回值
函數(shù)原形:
BOOL GetExitCodeThread( HANDLE hThread, LPDWORD lpExitCode);
參數(shù):
hThread 指向欲獲取返回值的線程對(duì)象的句柄
lpExitCode 用于存儲(chǔ)線程的返回值
返回值:函數(shù)執(zhí)行成功則返回非0值,否則返回
0(FALSE)
第一個(gè)參數(shù)是線程句柄,用
CreateThread 創(chuàng)建線程時(shí)獲得到。
第二個(gè)參數(shù)是一個(gè) DWORD的指針,用戶應(yīng)該使用一個(gè) DWORD 類型的變量去接收數(shù)據(jù),返回的數(shù)據(jù)是線程的退出碼,
通過(guò)線程退出碼可以判斷線程是否正在運(yùn)行,還是已經(jīng)退出。或者可以判斷線程是否是正常退出還是異常退出。
執(zhí)行成功時(shí),存放線程的狀態(tài)碼,如果是線程的返回值,表示線程執(zhí)行完, 如果線程沒執(zhí)行完,返回STILL_ACTIVE,如果線程的返回值就是STILL_ACTIVE,就無(wú)法判斷 .
MSDN解釋:GetExitCodeThread Function
Retrieves the termination status of the specified thread.
BOOL WINAPI GetExitCodeThread( __in HANDLE hThread, __out LPDWORD lpExitCode );
Parameters
- hThread
-
A handle to the thread.
The handle must have the THREAD_QUERY_INFORMATION access right. For more information, see Thread Security and Access Rights.
- lpExitCode
-
A pointer to a variable to receive the thread termination status. If the specified thread has not terminated and the function succeeds, the termination status returned is STILL_ACTIVE.
Return Value
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
Remarks
If the thread has terminated and the function succeeds, the termination status returned may be one of the following:
- The exit value specified in the ExitThread or TerminateThread function.
- The return value from the thread function.
- The exit value of the thread's process.
Warning If a thread happens to return STILL_ACTIVE (259) as an error code, applications that test for this value could end up in an infinite loop.
參考例子:
1 int main()
2 {
3 DWORD exitCode1 = 0;
4 DWORD exitCode2 = 0;
5 DWORD threadId;
6
7 HANDLE hThrd1 = CreateThread(NULL, 0, ThreadFunc1, 0, 0, &threadId );
9 if (hThrd1)
10 printf("Thread 1 launched\n");
11
13 HANDLE hThrd2 = CreateThread(NULL, 0, ThreadFunc2, 0, 0, &threadId );
14 if (hThrd2)
15 printf("Thread 2 launched\n");
16
18 for (;;)
19 {
20 printf("Press any key to exit..\n");
21 getch();
22 GetExitCodeThread(hThrd1, &exitCode1);
23 GetExitCodeThread(hThrd2, &exitCode2);
24 if ( exitCode1 == STILL_ACTIVE )
25 puts("Thread 1 is still running!");
26
27 if ( exitCode2 == STILL_ACTIVE )
28 puts("Thread 2 is still running!");
29 if ( exitCode1 != STILL_ACTIVE && exitCode2 != STILL_ACTIVE )
30 break;
31 }
32
33 CloseHandle(hThrd1);
34 CloseHandle(hThrd2);
35
36 printf("Thread 1 returned %d\n", exitCode1);
37 printf("Thread 2 returned %d\n", exitCode2);
38 return EXIT_SUCCESS;
39 }
40
41 DWORD WINAPI ThreadFunc1(LPVOID n)
42 {
43 Sleep((DWORD)n*1000*2);
44 return (DWORD)n * 10;
45 }
46
48 DWORD WINAPI ThreadFunc2(LPVOID n)
49 {
50 Sleep((DWORD)n*1000*2);
51 return (DWORD)n * 10;
52 }
以上是源碼及演示程序下載地址
Introduction 介紹
這篇文章描述的是一個(gè)可以用于在對(duì)話框上顯示各種主流類型圖片 (如 BMP, GIF, JPEG...) 的MFC控件
Background 背景
我花了一些時(shí)間去搜索可以用于顯示圖片的MFC控件, 但卻沒有發(fā)現(xiàn)合適的。 所以我決定自己做一個(gè)輕量級(jí),靈活度高的圖片控件(Picture control)去顯示各種類型的圖片。
Using the code 如何使用
這個(gè)控件內(nèi)部使用的是GDI+庫(kù),所以請(qǐng)?jiān)谑褂脮r(shí)把GdiPlus.lib加入到你的工程中(include libraries)。
使用這個(gè)控件時(shí),先用VC++對(duì)話框設(shè)計(jì)器創(chuàng)建一個(gè)靜態(tài)文字控件(static text control) 。之后用MFC向?qū)檫@個(gè)控件分配一個(gè)控件變量,類型定義為CPictureCtrl。
現(xiàn)在你可以用你的控件裝載顯示圖片了,你只需要在這幾個(gè)CPictureCtrl::LoadFrom...
函數(shù), 選擇合適你需要的的進(jìn)行調(diào)用。裝載后控件會(huì)自動(dòng)更新并顯示圖片。
要清除掉控件中顯示的圖片,調(diào)用CPictureCtrl::FreeImage
即可。
你的圖片會(huì)被自動(dòng)調(diào)整到控件的大小,這可能會(huì)改變圖片原先的長(zhǎng)寬比例。
Collapse |
Copy Codeclass CPictureCtrl :
public CStatic
{
public:
//Constructor
CPictureCtrl(void);
//Destructor
~CPictureCtrl(void);
public:
//Loads an image from a file
BOOL LoadFromFile(CString &szFilePath);
//Loads an image from an IStream interface
BOOL LoadFromStream(IStream* piStream);
//Loads an image from a byte stream;
BOOL LoadFromStream(BYTE* pData, size_t nSize);
//Loads an image from a Resource
// BOOL LoadFromResource(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType);
//Overload - Single load function
BOOL Load(CString &szFilePath);
BOOL Load(IStream* piStream);
BOOL Load(BYTE* pData, size_t nSize);
// BOOL Load(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType);
//Frees the image data
void FreeData();
protected:
virtual void PreSubclassWindow();
//Draws the Control
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
virtual BOOL OnEraseBkgnd(CDC* pDC);
private:
//Internal image stream buffer
IStream* m_pStream;
//Control flag if a pic is loaded
BOOL m_bIsPicLoaded;
//GDI Plus Token
ULONG_PTR m_gdiplusToken; };
Points of interest
這個(gè)控件是基于
CStatic
control 設(shè)計(jì)的(基類使用的是CStatic)。所以你可以使用CStatic control的各種功能,但它并不會(huì)顯示任何文字。對(duì)GDI+庫(kù)的使用使其可以支持各種主流類型的圖片。
History 歷史
License
About the Author
TEiseler
Tester / Quality Assurance GermanyMember |
|
本文轉(zhuǎn)自:
http://blog.csdn.net/cashey1991/article/details/7516996
重載 DrawItem 函數(shù)或Onpaint函數(shù)
void CListCtrlCl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// 重繪網(wǎng)格線 [7/12/2013 dell]
const MSG *msg = GetCurrentMessage();
DefWindowProc( msg->message, msg->wParam, msg->lParam );
// Draw the lines only for LVS_REPORT mode
if( (GetStyle() & LVS_TYPEMASK) == LVS_REPORT )
{
// Get the number of columns
CClientDC dc(this );
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
// The bottom of the header corresponds to the top of the line
RECT rect, rectCol;
pHeader->GetClientRect( &rect );
int top = rect.bottom;
// Now get the client rect so we know the line length and
// when to stop
GetClientRect( &rect );
if( !GetItemRect( 1, &rectCol, LVIR_BOUNDS ))
return;
int height1 = rectCol.bottom - rectCol.top;
// The border of the column is offset by the horz scroll
int borderx = 0 - GetScrollPos( SB_HORZ );
for( int i = 0; i < nColumnCount; i++ )
{
// Get the next border
borderx += GetColumnWidth( i );
// if next border is outside client area, break out
if( borderx >= rect.right ) break;
// Draw the line.
dc.MoveTo( borderx-1, top/*top*/);
dc.LineTo( borderx-1, (2+lpDrawItemStruct->itemID)*height1/*rect.bottom*/ );
}
// Draw the horizontal grid lines
// First get the height
if( !GetItemRect( 0, &rect, LVIR_BOUNDS ))
return;
int height = rect.bottom - rect.top;
GetClientRect( &rect );
int width = rect.right;
for(int i = 1; i <= lpDrawItemStruct->itemID+1; i++ )
{
dc.MoveTo( 0, top + height*i);
dc.LineTo( width, top + height*i );
}
}
}
本文轉(zhuǎn)自:http://www.codeguru.com/cpp/controls/listview/gridlines/article.php/c963/Drawing-horizontal-and-vertical-gridlines.htm
示例代碼:
Sock_UDP::Sock_UDP()
{
m_socket = INVALID_SOCKET;
WSAData ws;
//每個(gè)Winsock程序必須使用WSAStartup載入合適的Winsock動(dòng)態(tài)鏈接庫(kù),如果載入失敗,WSAStartup將返回SOCKET_ERROR,這個(gè)錯(cuò)誤就是WSANOTINITIALISED
if (WSAStartup(MAKEWORD(2,2),&ws)!=0)
{
LOG("WSAStartup failed! Error: %d", WSAGetLastError());
}
}
Sock_UDP::~Sock_UDP()
{
}
/******************************************************************
* 函數(shù)介紹:對(duì)連接進(jìn)行初始化
* 輸入?yún)?shù): strIPAddress:廣播的ip地址,strPort: 端口號(hào)
* 輸出參數(shù):
* 返回值 :
*******************************************************************/
BOOL Sock_UDP::InitSocket(const CString strIPAddress,const CString strPort)
{
//創(chuàng)建套接字,ipv4,報(bào)文,udp協(xié)議
m_socket = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
//創(chuàng)建socket失敗
if (INVALID_SOCKET == m_socket)
{
LOG("failed to create socket");
return false;
}
//初始化sock地址
InitSockAddress(strIPAddress,strPort);
// 綁定
if (bind (m_socket,
(struct sockaddr FAR *) &m_sockLocalAddress,
sizeof (m_sockLocalAddress)) == SOCKET_ERROR)
{
//報(bào)錯(cuò)
LOG("Binding socket failed! Error: %d" ,WSAGetLastError());
closesocket (m_socket);
return false;
}
int iOptVal=64; //1秒
// 設(shè)置組播存活時(shí)間
if (setsockopt (m_socket,
IPPROTO_IP,
3,
(char FAR *)&iOptVal,
sizeof (int)) == SOCKET_ERROR)
{
//報(bào)錯(cuò)
LOG("setsockopt failed! Error: %d" ,WSAGetLastError());
closesocket (m_socket);
return false;
}
return true;
}
/******************************************************************
* 函數(shù)介紹:關(guān)閉socket
* 輸入?yún)?shù):
* 輸出參數(shù):
* 返回值 :
*******************************************************************/
void Sock_UDP::CleanSocket()
{
shutdown(m_socket,0x01);
closesocket(m_socket);
WSACleanup();
}
/******************************************************************
* 函數(shù)介紹:初始化IP組播地址和端口
* 輸入?yún)?shù):strIPAddress:ip地址,strPort: 端口號(hào)
* 輸出參數(shù):
* 返回值 :
*******************************************************************/
void Sock_UDP::InitSockAddress(const CString strIPAddress,const CString strPort)
{
//本地sock地址設(shè)置
m_sockLocalAddress.sin_family = AF_INET; //ipv4地址類型
m_sockLocalAddress.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
m_sockLocalAddress.sin_port = htons(0);
memset(m_sockLocalAddress.sin_zero,0,8);
//目的sock地址設(shè)置
//IPv4版本
m_sockDesAddress.sin_family = AF_INET;
//端口
m_sockDesAddress.sin_port = htons (atol(strPort));
//地址
m_sockDesAddress.sin_addr.s_addr = inet_addr (strIPAddress);
}
動(dòng)態(tài)創(chuàng)建視圖時(shí)候 AfxCheckDialogTemplate執(zhí)行出錯(cuò)
在mfc的sdi架構(gòu)中,準(zhǔn)備多做幾個(gè)視圖,試圖類繼承自formview,但在動(dòng)態(tài)創(chuàng)建視圖的時(shí)候出了錯(cuò)誤,AfxCheckDialogTemplate執(zhí)行出錯(cuò)。后來(lái)通過(guò)搜索發(fā)現(xiàn)cformview類關(guān)聯(lián)對(duì)話框時(shí)候,資源必須具備child屬性。
1.CFormView類關(guān)聯(lián)的對(duì)話框資源必須具有Child屬性。
由CFormView派生的類,可以關(guān)聯(lián)一個(gè)對(duì)話框資源。但該對(duì)話框資源必須在屬性設(shè)定中Style選定[Child]屬性,否則的話,
代碼可以編譯,但Debug運(yùn)行會(huì)報(bào)告一個(gè)斷言錯(cuò)誤,跟蹤代碼,斷言在:
#ifdef _DEBUG
// dialog template must exist and be invisible with WS_CHILD set
if (!_AfxCheckDialogTemplate(m_lpszTemplateName, TRUE))
{
ASSERT(FALSE); // invalid dialog template name
PostNcDestroy(); // cleanup if Create fails too soon
return FALSE;
}
#endif //_DEBUG
2.CFormView比較特殊,是一個(gè)父窗體嵌套了一個(gè)子窗體,所以,
CFormView類的派生類的實(shí)例不響應(yīng)WM_CLOSE消息,僅僅響應(yīng)WM_DESTROY消息。
另外,若要用代碼關(guān)閉當(dāng)前View,也不能直接:PostMessage(WM_CLOSE,0,0);
而必須先獲取父窗體的指針,然后對(duì)父窗體發(fā)送WM_CLOSE消息才行,像這樣:
GetParent()->PostMessage(WM_CLOSE,0,0);
才能夠達(dá)到目的。
《深入淺出MFC》第八章461頁(yè)圖8-1清楚地說(shuō)明了這種情況,View窗口是CChildFrame窗口的子窗口。