添加消息响应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,"华文行楷"); //设置字体大小和类型
if(pWnd->GetDlgCtrlID()==IDC_STATIC01)//可以用CTLCOLOR_STATIC表示静态控件
{
pDC->SelectObject(&m_font); //设置字体
pDC->SetTextColor(RGB(0,0,255)); //设置字体颜色
pDC->SetBkMode(TRANSPARENT); //属性设置为透明
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);
}
}
posted @
2013-08-18 16:54 王海光 阅读(11125) |
评论 (0) |
编辑 收藏
采用TCP连接的C/S模式软件,连接的双方在连接空闲状态时,如果任意一方意外崩溃、当机、网线断开或路由器故障,另一方无法得知TCP连接已经失效,除非继续在此连接上发送数据导致错误返回。很多时候,这不是我们需要的。我们希望服务器端和客户端都能及时有效地检测到连接失效,然后优雅地完成一些清理工作并把错误报告给用户。
如何及时有效地检测到一方的非正常断开,一直有两种技术可以运用。一种是由TCP协议层实现的Keepalive,另一种是由应用层自己实现的心跳包。
TCP默认并不开启Keepalive功能,因为开启Keepalive功能需要消耗额外的宽带和流量,尽管这微不足道,但在按流量计费的环境下增加了费用,另一方面,Keepalive设置不合理时可能会因为短暂的网络波动而断开健康的TCP连接。并且,默认的Keepalive超时需要7,200,000 milliseconds,即2小时,探测次数为5次。
对于Win2K/XP/2003,可以从下面的注册表项找到影响整个系统所有连接的keepalive参数:
[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Tcpip/Parameters]
"KeepAliveTime”=dword:006ddd00
"KeepAliveInterval"=dword:000003e8
"MaxDataRetries"="5"
对于实用的程序来说,2小时的空闲时间太长。因此,我们需要手工开启Keepalive功能并设置合理的Keepalive参数。
// 开启KeepAlive
BOOL bKeepAlive = TRUE;
int nRet = ::setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
if (nRet == SOCKET_ERROR)
{
return FALSE;
}
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
// 设置KeepAlive参数
tcp_keepalive alive_in = {0};
tcp_keepalive alive_out = {0};
alive_in.keepalivetime = 5000; // 开始首次KeepAlive探测前的TCP空闭时间
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
alive_in.keepaliveinterval = 1000; // 两次KeepAlive探测间的时间间隔
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
alive_in.onoff = TRUE;
unsigned long ulBytesReturn = 0;
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
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;
![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif)
}
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
开启Keepalive选项之后,对于使用IOCP模型的服务器端程序来说,一旦检测到连接断开,GetQueuedCompletionStatus函数将立即返回FALSE,使得服务器端能及时清除该连接、释放该连接相关的资源。对于使用select模型的客户端来说,连接断开被探测到时,以recv目的阻塞在socket上的select方法将立即返回SOCKET_ERROR,从而得知连接已失效,客户端程序便有机会及时执行清除工作、提醒用户或重新连接。
另一种技术,由应用程序自己发送心跳包来检测连接的健康性。客户端可以在一个Timer中或低级别的线程中定时向发服务器发送一个短小精悍的包,并等待服务器的回应。客户端程序在一定时间内没有收到服务器回应即认为连接不可用,同样,服务器在一定时间内没有收到客户端的心跳包则认为客户端已经掉线。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
windows下此处的”非正常断开”指TCP连接不是以优雅的方式断开,如网线故障等物理链路的原因,还有突然主机断电等原因.
有两种方法可以检测:
1.TCP连接双方定时发握手消息
2.利用TCP协议栈中的KeepAlive探测
第二种方法简单可靠,只需对TCP连接两个Socket设定KeepAlive探测,
所以本文只讲第二种方法在Linux,Window2000下的实现(在其它的平台上没有作进一步的测试)
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
Windows 2000平台下 头文件
#include <mstcpip.h>
//定义结构及宏
/*
struct TCP_KEEPALIVE {
u_longonoff;
u_longkeepalivetime;
u_longkeepaliveinterval;
} ;
*/
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
tcp_keepalive live,liveout;
live.keepaliveinterval=5000; //每5秒发一次探测报文,发5次没有回应,就断开
live.keepalivetime=30000;//超过30s没有数据,就发送控测包
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){
}
}![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
ACE下代码 //by rainfish blog.csdn.net/bat603
int Opt = 1;
//在测试过程中,发现检测的次数是5次,即下面的设置中,从最近一次消息开始计算的10秒后,每次间隔5秒,连续发送5次,即35秒发现网络断了
tcp_keepalive live,liveout;
live.keepaliveinterval=5000; //每次检测的间隔 (单位毫秒)
live.keepalivetime=10000; //第一次开始发送的时间(单位毫秒)
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平台下
#include "/usr/include/linux/tcp.h"
#include "/usr/include/linux/socket.h"
////KeepAlive实现,单位秒
//下面代码要求有ACE,如果没有包含ACE,则请把用到的ACE函数改成linux相应的接口
int keepAlive = 1;//设定KeepAlive
int keepIdle = 5;//开始首次KeepAlive探测前的TCP空闭时间
int keepInterval = 5;//两次KeepAlive探测间的时间间隔
int keepCount = 3;//判定断开前的KeepAlive探测次数
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")));
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
本文转自:
http://www.cppblog.com/API/archive/2013/08/13/202516.html
posted @
2013-08-14 19:14 王海光 阅读(1165) |
评论 (0) |
编辑 收藏
API:要取得屏幕大小,可以用下面几个函数:
int cx = GetSystemMetrics( SM_CXFULLSCREEN );
int cy = GetSystemMetrics( SM_CYFULLSCREEN );
通过上边两个函数获取的是 显示屏幕的大小,但不包括任务栏等区域。
int cx = GetSystemMetrics( SM_CXSCREEN );
int cy = GetSystemMetrics( SM_CYSCREEN );
这两个函数获取的是真正屏幕的大小。
MFC:
HDC hDC = ::GetDC(HWND(NULL)); // 得到屏幕DC
int x = ::GetDeviceCaps(hDC,HORZRES); // 宽
int y = ::GetDeviceCaps(hDC,VERTRES); // 高
::ReleaseDC(HWND(NULL),hDC); // 释放DC
posted @
2013-08-13 14:09 王海光 阅读(6382) |
评论 (0) |
编辑 收藏
MFC程序vs2008编译通过,运行时出错,无法打开,提示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);
}
红色行就是出错地方,原因是资源文件引起,一般是从英文或其它语言改成简体中文而造成,把语言相关改为以下几行#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
#ifdef _WIN32
LANGUAGE 4, 2
#pragma code_page(936)
#endif //_WIN32
posted @
2013-08-12 17:41 王海光 阅读(1197) |
评论 (0) |
编辑 收藏
Linux下的段错误产生的原因及调试方法 原文地址:http://www.upsdn.net/html/2006-11/775.html
参考地址:http://www.cnblogs.com/khler/archive/2010/09/16/1828349.html
简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址.
一般来说,段错误就是指访问的内存超出了系统所给这个程序的内存空间,通常这个值是由gdtr来保存的,他是一个48位的寄存器,其中的32位是保存由它指向的gdt表,后13位保存相应于gdt的下标,最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向的gdt是由以64位为一个单位的表,在这张表中就保存着程序运行的代码段以及数据段的起始地址以及与此相应的段限和页面交换还有程序运行级别还有内存粒度等等的信息。一旦一个程序发生了越界访问,cpu就会产生相应的异常保护,于是segmentation fault就出现了.
在编程中以下几类做法容易导致段错误,基本是是错误地使用指针引起的
1)访问系统数据区,尤其是往 系统保护的内存地址写数据
最常见就是给一个指针以0地址
2)内存越界(数组越界,变量类型不一致等) 访问到不属于你的内存区域
解决方法
我们在用C/C++语言写程序的时侯,内存管理的绝大部分工作都是需要我们来做的。实际上,内存管理是一个比较繁琐的工作,无论你多高明,经验多丰富,难 免会在此处犯些小错误,而通常这些错误又是那么的浅显而易于消除。但是手工“除虫”(debug),往往是效率低下且让人厌烦的,本文将就"段错误"这个 内存访问越界的错误谈谈如何快速定位这些"段错误"的语句。
下面将就以下的一个存在段错误的程序介绍几种调试方法:
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 }
|
作为一个熟练的C/C++程序员,以上代码的bug应该是很清楚的,因为它尝试操作地址为0的内存区域,而这个内存区域通常是不可访问的禁区,当然就会出错了。我们尝试编译运行它:
xiaosuo@gentux test $ ./a.out 段错误
|
果然不出所料,它出错并退出了。
1.利用gdb逐步查找段错误:这种方法也是被大众所熟知并广泛采用的方法,首先我们需要一个带有调试信息的可执行程序,所以我们加上“-g -rdynamic"的参数进行编译,然后用gdb调试运行这个新编译的程序,具体步骤如下:
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)
|
哦?!好像不用一步步调试我们就找到了出错位置d.c文件的第4行,其实就是如此的简单。
从这里我们还发现进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man 7 signal),我们知道SIGSEGV默认handler的动作是打印”段错误"的出错信息,并产生Core文件,由此我们又产生了方法二。
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)。不过奇怪了,我的系统上并没有找到core文件。后来,忆起为了渐少系统上的拉圾文件的数量(本人有些洁癖,这也是我喜欢Gentoo的原因 之一),禁止了core文件的生成,查看了以下果真如此,将系统的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 段错误 (core dumped) xiaosuo@gentux test $ ls a.out core d.c f.c g.c pango.c test_iconv.c test_regex.c
|
core文件终于产生了,用gdb调试一下看看吧:
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: 输入/输出错误. 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;
|
哇,好历害,还是一步就定位到了错误所在地,佩服一下Linux/Unix系统的此类设计。
接着考虑下去,以前用windows系统下的ie的时侯,有时打开某些网页,会出现“运行时错误”,这个时侯如果恰好你的机器上又装有windows的编译器的话,他会弹出来一个对话框,问你是否进行调试,如果你选择是,编译器将被打开,并进入调试状态,开始调试。
Linux下如何做到这些呢?我的大脑飞速地旋转着,有了,让它在SIGSEGV的handler中调用gdb,于是第三个方法又诞生了:
3.段错误时启动调试:#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; }
|
编译运行效果如下:
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
|
怎么样?是不是依旧很酷?
以上方法都是在系统上有gdb的前提下进行的,如果没有呢?其实glibc为我们提供了此类能够dump栈内容的函数簇,详见/usr/include/execinfo.h(这些函数都没有提供man page,难怪我们找不到),另外你也可以通过
gnu的手册进行学习。
4.利用backtrace和objdump进行分析:重写的代码如下:
#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; }
|
编译运行结果如下:
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]
|
这次你可能有些失望,似乎没能给出足够的信息来标示错误,不急,先看看能分析出来什么吧,用objdump反汇编程序,找到地址0x804876f对应的代码位置:
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
|
我们还是找到了在哪个函数(dummy_function)中出错的,信息已然不是很完整,不过有总比没有好的啊!
后记:本文给出了分析"段错误"的几种方法,不要认为这是与孔乙己先生的"回"字四种写法一样的哦,因为每种方法都有其自身的适用范围和适用环境,请酌情使用,或遵医嘱。
部分资料来源于xiaosuo @ cnblog.cn, 特此致谢
作者:upsdn整理 更新日期:2006-11-03
来源:upsdn.net 浏览次数:
其它调试办法
在Linux上的使用开源C++日志库---log4cplus
linux下追踪函数调用堆栈
一般察看函数运行时堆栈的方法是使用GDB之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的。
在头文件"execinfo.h"中声明了三个函数用于获取当前线程的函数调用堆栈
Function: int backtrace(void **buffer,int size)
该函数用与获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针列表。参数 size 用来指定buffer中可以保存多少个void* 元素。函数返回值是实际获取的指针个数,最大不超过size大小
在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址
注意某些编译器的优化选项对获取正确的调用堆栈有干扰,另外内联函数没有堆栈框架;删除框架指针也会使无法正确解析堆栈内容
Function: char ** backtrace_symbols (void *const *buffer, int size)
backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组. 参数buffer应该是从backtrace函数获取的数组指针,size是该数组中的元素个数(backtrace的返回值)
函数返回值是一个指向字符串数组的指针,它的大小同buffer相同.每个字符串包含了一个相对于buffer中对应元素的可打印信息.它包括函数名,函数的偏移地址,和实际的返回地址
现在,只有使用ELF二进制格式的程序和苦衷才能获取函数名称和偏移地址.在其他系统,只有16进制的返回地址能被获取.另外,你可能需要传递相应的标志给链接器,以能支持函数名功能(比如,在使用GNU ld的系统中,你需要传递(-rdynamic))
该函数的返回值是通过malloc函数申请的空间,因此调用这必须使用free函数来释放指针.
注意:如果不能为字符串获取足够的空间函数的返回值将会为NULL
Function:void backtrace_symbols_fd (void *const *buffer, int size, int fd)
backtrace_symbols_fd与backtrace_symbols 函数具有相同的功能,不同的是它不会给调用者返回字符串数组,而是将结果写入文件描述符为fd的文件中,每个函数对应一行.它不需要调用malloc函数,因此适用于有可能调用该函数会失败的情况。
#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;
}
编译运行的结果如下: # 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.
posted @
2013-07-31 12:07 王海光 阅读(825) |
评论 (0) |
编辑 收藏
GetExitCodeThread函数是获得线程的退出码,
函数:
GetExitCodeThread()
功能:获取一个结束线程的返回值
函数原形:
BOOL GetExitCodeThread( HANDLE hThread, LPDWORD lpExitCode);
参数:
hThread 指向欲获取返回值的线程对象的句柄
lpExitCode 用于存储线程的返回值
返回值:函数执行成功则返回非0值,否则返回
0(FALSE)
第一个参数是线程句柄,用
CreateThread 创建线程时获得到。
第二个参数是一个 DWORD的指针,用户应该使用一个 DWORD 类型的变量去接收数据,返回的数据是线程的退出码,
通过线程退出码可以判断线程是否正在运行,还是已经退出。或者可以判断线程是否是正常退出还是异常退出。
执行成功时,存放线程的状态码,如果是线程的返回值,表示线程执行完, 如果线程没执行完,返回STILL_ACTIVE,如果线程的返回值就是STILL_ACTIVE,就无法判断 .
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 }
posted @
2013-07-31 11:37 王海光 阅读(5705) |
评论 (0) |
编辑 收藏
以上是源码及演示程序下载地址
Introduction 介绍
这篇文章描述的是一个可以用于在对话框上显示各种主流类型图片 (如 BMP, GIF, JPEG...) 的MFC控件
Background 背景
我花了一些时间去搜索可以用于显示图片的MFC控件, 但却没有发现合适的。 所以我决定自己做一个轻量级,灵活度高的图片控件(Picture control)去显示各种类型的图片。
Using the code 如何使用
这个控件内部使用的是GDI+库,所以请在使用时把GdiPlus.lib加入到你的工程中(include libraries)。
使用这个控件时,先用VC++对话框设计器创建一个静态文字控件(static text control) 。之后用MFC向导为这个控件分配一个控件变量,类型定义为CPictureCtrl。
现在你可以用你的控件装载显示图片了,你只需要在这几个CPictureCtrl::LoadFrom...
函数, 选择合适你需要的的进行调用。装载后控件会自动更新并显示图片。
要清除掉控件中显示的图片,调用CPictureCtrl::FreeImage
即可。
你的图片会被自动调整到控件的大小,这可能会改变图片原先的长宽比例。
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
这个控件是基于
CStatic
control 设计的(基类使用的是CStatic)。所以你可以使用CStatic control的各种功能,但它并不会显示任何文字。对GDI+库的使用使其可以支持各种主流类型的图片。
History 历史
License
About the Author
TEiseler
Tester / Quality Assurance GermanyMember |
|
本文转自:
http://blog.csdn.net/cashey1991/article/details/7516996
posted @
2013-07-22 18:09 王海光 阅读(2036) |
评论 (0) |
编辑 收藏
重载 DrawItem 函数或Onpaint函数
void CListCtrlCl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// 重绘网格线 [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 );
}
}
}
本文转自:http://www.codeguru.com/cpp/controls/listview/gridlines/article.php/c963/Drawing-horizontal-and-vertical-gridlines.htm
posted @
2013-07-12 11:10 王海光 阅读(2574) |
评论 (0) |
编辑 收藏
示例代码:
Sock_UDP::Sock_UDP()
{
m_socket = INVALID_SOCKET;
WSAData ws;
//每个Winsock程序必须使用WSAStartup载入合适的Winsock动态链接库,如果载入失败,WSAStartup将返回SOCKET_ERROR,这个错误就是WSANOTINITIALISED
if (WSAStartup(MAKEWORD(2,2),&ws)!=0)
{
LOG("WSAStartup failed! Error: %d", WSAGetLastError());
}
}
Sock_UDP::~Sock_UDP()
{
}
/******************************************************************
* 函数介绍:对连接进行初始化
* 输入参数: strIPAddress:广播的ip地址,strPort: 端口号
* 输出参数:
* 返回值 :
*******************************************************************/
BOOL Sock_UDP::InitSocket(const CString strIPAddress,const CString strPort)
{
//创建套接字,ipv4,报文,udp协议
m_socket = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
//创建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)
{
//报错
LOG("Binding socket failed! Error: %d" ,WSAGetLastError());
closesocket (m_socket);
return false;
}
int iOptVal=64; //1秒
// 设置组播存活时间
if (setsockopt (m_socket,
IPPROTO_IP,
3,
(char FAR *)&iOptVal,
sizeof (int)) == SOCKET_ERROR)
{
//报错
LOG("setsockopt failed! Error: %d" ,WSAGetLastError());
closesocket (m_socket);
return false;
}
return true;
}
/******************************************************************
* 函数介绍:关闭socket
* 输入参数:
* 输出参数:
* 返回值 :
*******************************************************************/
void Sock_UDP::CleanSocket()
{
shutdown(m_socket,0x01);
closesocket(m_socket);
WSACleanup();
}
/******************************************************************
* 函数介绍:初始化IP组播地址和端口
* 输入参数:strIPAddress:ip地址,strPort: 端口号
* 输出参数:
* 返回值 :
*******************************************************************/
void Sock_UDP::InitSockAddress(const CString strIPAddress,const CString strPort)
{
//本地sock地址设置
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地址设置
//IPv4版本
m_sockDesAddress.sin_family = AF_INET;
//端口
m_sockDesAddress.sin_port = htons (atol(strPort));
//地址
m_sockDesAddress.sin_addr.s_addr = inet_addr (strIPAddress);
}
posted @
2013-06-19 18:00 王海光 阅读(6854) |
评论 (0) |
编辑 收藏
动态创建视图时候 AfxCheckDialogTemplate执行出错
在mfc的sdi架构中,准备多做几个视图,试图类继承自formview,但在动态创建视图的时候出了错误,AfxCheckDialogTemplate执行出错。后来通过搜索发现cformview类关联对话框时候,资源必须具备child属性。
1.CFormView类关联的对话框资源必须具有Child属性。
由CFormView派生的类,可以关联一个对话框资源。但该对话框资源必须在属性设定中Style选定[Child]属性,否则的话,
代码可以编译,但Debug运行会报告一个断言错误,跟踪代码,断言在:
#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比较特殊,是一个父窗体嵌套了一个子窗体,所以,
CFormView类的派生类的实例不响应WM_CLOSE消息,仅仅响应WM_DESTROY消息。
另外,若要用代码关闭当前View,也不能直接:PostMessage(WM_CLOSE,0,0);
而必须先获取父窗体的指针,然后对父窗体发送WM_CLOSE消息才行,像这样:
GetParent()->PostMessage(WM_CLOSE,0,0);
才能够达到目的。
《深入浅出MFC》第八章461页图8-1清楚地说明了这种情况,View窗口是CChildFrame窗口的子窗口。
posted @
2013-05-31 17:02 王海光 阅读(1974) |
评论 (0) |
编辑 收藏