模拟键盘我们用Keybd_event这个api函数,模拟鼠标按键用mouse_event函数。在VC里调用api函数是既简单又方便不过的事了。 首先介绍一下Keybd_event函数。Keybd_event能触发一个按键事件,也就是说回产生一个WM_KEYDOWN或WM_KEYUP消息。当然也可以用产生这两个消息来模拟按键,但是没有直接用这个函数方便。Keybd_event共有四个参数,第一个为按键的虚拟键值,如回车键为vk_return, tab键为vk_tab。第二个参数为扫描码,一般不用设置,用0代替就行第三个参数为选项标志,如果为keydown则置0即可,如果为keyup则设成“KEYEVENTF_KEYUP”,第四个参数一般也是置0即可。用如下代码即可实现模拟按下键,其中的XX表示XX键的虚拟键值,在这里也就是各键对应的键码,如’A’=65 keybd_event(65,0,0,0); keybd_event(65,0,KEYEVENTF_KEYUP,0); …
堆和栈的区别
在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。
首先,我们举一个例子:
1
void f() { int* p=new int[5]; }
1
2
3
4
5
600401028 push 14h
0040102A call operator new (00401060)
0040102F add esp,4
00401032 mov dword ptr [ebp-8],eax
00401035 mov eax,dword ptr [ebp-8]
00401038 mov dword ptr [ebp-4],eaxdelete []p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的Cookie信息去进行释放内存的工作。
好了,我们回到我们的主题:堆和栈究竟有什么区别?
主要的区别由以下几点:
1、管理方式不同;
2、空间大小不同;
3、能否产生碎片不同;
4、生长方向不同;
5、分配方式不同;
6、分配效率不同;
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改:
打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。
注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。 碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。 生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。 分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。 分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。 从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。 虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。 无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难的:) 对了,还有一件事,如果有人把堆栈合起来说,那它的意思是栈,可不是堆,呵呵,清楚了?
获取CPU序列号
//取得IntelCPU的ID号:
void IntelCPUIDDlg::OnButtonCPUID() { unsigned long s1,s2; unsigned char vendor_id[]=”——————“; CString str1,str2,str3; __asm { xor eax,eax cpuid mov dword ptr vendor_id,ebx mov dword ptr vendor_id[+4],edx mov dword ptr vendor_id[+8],ecx } str1.Format(“%s”,vendor_id); __asm //取得CPU ID的高32位 { mov eax,01h xor edx,edx cpuid mov s2,eax } str2.Format(“%08X-“,s2);
__asm //取得CPU ID的低64位 { mov eax,03h xor ecx,ecx xor edx,edx cpuid mov s1,edx mov s2,ecx }
str3.Format(“%08X-%08X\n”,s1,s2); str2+=str3; m_editVendor.SetWindowText(str1); m_editCPUID.SetWindowText(str2); }
获取磁盘使用空间和剩余空间
char crDrv[3]; CStringArray strArray; _ULARGE_INTEGER n64TotalNumberOfBytes, n64TotalNumberOfFreeBytes; __int64 n64TotalFree = 0,n64TotalSpace = 0; float fTotalFree,fTotalSpace,fTempFree,fTempTotal; ULONG lTotalFree = 0,lTotalSpace = 0, lTempFree = 0,lTempTotal = 0; CString strInfo; BOOL bSuccess;
for(int n=2;n<=25;n++) { CString str; str.Format(“%c:\“,n+’A’); strcpy(crDrv,str); UINT nDrvType = GetDriveType(crDrv); switch(nDrvType) { case DRIVE_FIXED: { strArray.Add(crDrv); break; } default: { break; } } }
for(n = 0;n < strArray.GetSize();n ++) { bSuccess = SHGetDiskFreeSpace(strArray.GetAt(n), NULL,&n64TotalNumberOfBytes;, &n64TotalNumberOfFreeBytes;); n64TotalSpace += n64TotalNumberOfBytes.QuadPart; n64TotalFree += n64TotalNumberOfFreeBytes.QuadPart;
lTempTotal = (ULONG)(n64TotalNumberOfBytes.QuadPart/1024); lTempFree = (ULONG)(n64TotalNumberOfFreeBytes.QuadPart/1024); fTempTotal = (float)lTempTotal; fTempTotal = fTempTotal/1024/1024; fTempFree = (float)lTempFree; fTempFree = fTempFree/1024/1024;
strInfo.Format(“%s 容量: %4.2fGB,可用空间: %4.2fGB.”, strArray.GetAt(n),fTempTotal,fTempFree); AfxMessageBox(strInfo); }
lTotalSpace = (ULONG)(n64TotalSpace/1024); lTotalFree = (ULONG)(n64TotalFree/1024); fTotalSpace = (float)lTotalSpace; fTotalSpace = fTotalSpace/1024/1024; fTotalFree = (float)lTotalFree; fTotalFree = fTotalFree/1024/1024;
strInfo.Format(“总容量: %4.2fGB,总共可用空间: %4.2fGB.”, fTotalSpace,fTotalFree); AfxMessageBox(strInfo);
怎么在一个静态Picture控件中显示JPG文件
my example: HBITMAP CPicture::LoadImageFromID(UINT nIDRes) { try { HMODULE hInst=GetModuleHandle(NULL); LPCTSTR lpRes = MAKEINTRESOURCE(nIDRes); HRSRC hRsrc = ::FindResource(hInst, lpRes, “JPG”); if (NULL == hRsrc) return FALSE;
HGLOBAL hGlobal = LoadResource(hInst, hRsrc);
if (NULL == hGlobal)
return FALSE;
DWORD dwSize = SizeofResource(hInst, hRsrc);
LPVOID lpData = LockResource(hGlobal);
if (NULL == lpData)
return FALSE;
// alloc memory based on file size
HGLOBAL hJPG = ::GlobalAlloc(GMEM_MOVEABLE, dwSize);
LPVOID lpJGP = ::GlobalLock(hJPG);
memcpy(lpJGP, lpData, dwSize);
::GlobalUnlock(hJPG);
//LPVOID pvData = GlobalLock(hGlobal);
//_ASSERTE(NULL != pvData);
// read file and store in global memory
LPSTREAM pstm = NULL;
// create IStream* from global memory
HRESULT hr = CreateStreamOnHGlobal(hJPG, TRUE, &pstm;);
_ASSERTE(SUCCEEDED(hr) && pstm);
// Create IPicture from image file
LPPICTURE gpPicture;
hr = ::OleLoadPicture(pstm, dwSize, FALSE, IID_IPicture, (LPVOID *)&gpPicture;);
_ASSERTE(SUCCEEDED(hr) && gpPicture);
pstm->Release();
OLE_HANDLE m_picHandle;
gpPicture->get_Handle(&m;_picHandle);
return (HBITMAP)m_picHandle;
}
catch (...)
{
}
return NULL;
} //call CPicture pic; HBITMAP hbmp=pic.LoadImageFromID(IDR_TODAY);//IDR_TODAY is jpg id (HBITMAP)::SendMessage(::GetDlgItem(this->m_hWnd,IDC_TODAY), STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp);
关于Debug和Release之本质区别的讨论
本文主要包含如下内容: 1. Debug 和 Release 编译方式的本质区别 2. 哪些情况下 Release 版会出错 3. 怎样“调试” Release 版的程序
一、Debug 和 Release 编译方式的本质区别
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
Debug 和 Release 的真正秘密,在于一组编译选项。下面列出了分别针对二者的选项(当然除此之外还有其他一些,如/Fd /Fo,但区别并不重要,通常他们也不会引起 Release 版错误,在此不讨论)
Debug 版本: /MDd /MLd 或 /MTd 使用 Debug runtime library(调试版本的运行时刻函数库) /Od 关闭优化开关 /D “_DEBUG” 相当于 #define _DEBUG,打开编译调试代码开关(主要针对 assert函数) /ZI 创建 Edit and continue(编辑继续)数据库,这样在调试过 程中如果修改了源代码不需重新编译 /GZ 可以帮助捕获内存错误 /Gm 打开最小化重链接开关,减少链接时间
Release 版本: /MD /ML 或 /MT 使用发布版本的运行时刻函数库 /O1 或 /O2 优化开关,使程序最小或最快 /D “NDEBUG” 关闭条件编译调试代码开关(即不编译assert函数) /GF 合并重复的字符串,并将字符串常量放到只读内存,防止 被修改
实际上,Debug 和 Release 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。
二、哪些情况下 Release 版会出错
有了上面的介绍,我们再来逐个对照这些选项看看 Release 版错误是怎样产生的
Runtime Library:链接哪种运行时刻函数库通常只对程序的性能产生影响。调试版本的 Runtime Library 包含了调试信息,并采用了一些保护机制以帮助发现错误,因此性能不如发布版本。编译器提供的 Runtime Library 通常很稳定,不会造成 Release 版错误;倒是由于 Debug 的 Runtime Library 加强了对错误的检测,如堆内存分配,有时会出现 Debug 有错但 Release 正常的现象。应当指出的是,如果 Debug 有错,即使 Release 正常,程序肯定是有 Bug 的,只不过可能是 Release 版的某次运行没有表现出来而已。
优化:这是造成错误的主要原因,因为关闭优化时源程序基本上是直接翻译的,而打开优化后编译器会作出一系列假设。这类错误主要有以下几种:
(1) 帧指针(Frame Pointer)省略(简称 FPO ):在函数调用过程中,所有调用信息(返回地址、参数)以及自动变量都是放在栈中的。若函数的声明与实现不同(参数、返回值、调用方式),就会产生错误————但 Debug 方式下,栈的访问通过 EBP 寄存器保存的地址实现,如果没有发生数组越界之类的错误(或是越界“不多”),函数通常能正常执行;Release 方式下,优化会省略 EBP 栈基址指针,这样通过一个全局指针访问栈就会造成返回地址错误是程序崩溃。C++ 的强类型特性能检查出大多数这样的错误,但如果用了强制类型转换,就不行了。你可以在 Release 版本中强制加入 /Oy- 编译选项来关掉帧指针省略,以确定是否此类错误。此类错误通常有:
● MFC 消息响应函数书写错误。正确的应为 afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam); ON_MESSAGE 宏包含强制类型转换。防止这种错误的方法之一是重定义 ON_MESSAGE 宏,把下列代码加到 stdafx.h 中(在#include “afxwin.h”之后),函数原形错误时编译会报错 #undef ON_MESSAGE #define ON_MESSAGE(message, memberFxn)
{ message, 0, 0, 0, AfxSig_lwl,
(AFX_PMSG)(AFX_PMSGW)(static_cast< LRESULT (AFX_MSG_CALL
CWnd::*)(WPARAM, LPARAM) > (&memberFxn;) },(2) volatile 型变量:volatile 告诉编译器该变量可能被程序之外的未知方式修改(如系统、其他进程和线程)。优化程序为了使程序性能提高,常把一些变量放在寄存器中(类似于 register 关键字),而其他进程只能对该变量所在的内存进行修改,而寄存器中的值没变。如果你的程序是多线程的,或者你发现某个变量的值与预期的不符而你确信已正确的设置了,则很可能遇到这样的问题。这种错误有时会表现为程序在最快优化出错而最小优化正常。把你认为可疑的变量加上 volatile 试试。
(3) 变量优化:优化程序会根据变量的使用情况优化变量。例如,函数中有一个未被使用的变量,在 Debug 版中它有可能掩盖一个数组越界,而在 Release 版中,这个变量很可能被优化调,此时数组越界会破坏栈中有用的数据。当然,实际的情况会比这复杂得多。与此有关的错误有: ● 非法访问,包括数组越界、指针错误等。例如 void fn(void) { int i; i = 1; int a[4]; { int j; j = 1; } a[-1] = 1;//当然错误不会这么明显,例如下标是变量 a[4] = 1; } j 虽然在数组越界时已出了作用域,但其空间并未收回,因而 i 和 j 就会掩盖越界。而 Release 版由于 i、j 并未其很大作用可能会被优化掉,从而使栈被破坏。
_DEBUG 与 NDEBUG :当定义了 _DEBUG 时,assert() 函数会被编译,而 NDEBUG 时不被编译。除此之外,VC++中还有一系列断言宏。这包括:
ANSI C 断言 void assert(int expression ); C Runtime Lib 断言 _ASSERT( booleanExpression ); _ASSERTE( booleanExpression ); MFC 断言 ASSERT( booleanExpression ); VERIFY( booleanExpression ); ASSERT_VALID( pObject ); ASSERT_KINDOF( classname, pobject ); ATL 断言 ATLASSERT( booleanExpression ); 此外,TRACE() 宏的编译也受 _DEBUG 控制。
所有这些断言都只在 Debug版中才被编译,而在 Release 版中被忽略。唯一的例外是 VERIFY() 。事实上,这些宏都是调用了 assert() 函数,只不过附加了一些与库有关的调试代码。如果你在这些宏中加入了任何程序代码,而不只是布尔表达式(例如赋值、能改变变量值的函数调用 等),那么 Release 版都不会执行这些操作,从而造成错误。初学者很容易犯这类错误,查找的方法也很简单,因为这些宏都已在上面列出,只要利用 VC++ 的 Find in Files 功能在工程所有文件中找到用这些宏的地方再一一检查即可。另外,有些高手可能还会加入 #ifdef _DEBUG 之类的条件编译,也要注意一下。 顺便值得一提的是 VERIFY() 宏,这个宏允许你将程序代码放在布尔表达式里。这个宏通常用来检查 Windows API 的返回值。有些人可能为这个原因而滥用 VERIFY() ,事实上这是危险的,因为 VERIFY() 违反了断言的思想,不能使程序代码和调试代码完全分离,最终可能会带来很多麻烦。因此,专家们建议尽量少用这个宏。
/GZ 选项:这个选项会做以下这些事
(1) 初始化内存和变量。包括用 0xCC 初始化所有自动变量,0xCD ( Cleared Data ) 初始化堆中分配的内存(即动态分配的内存,例如 new ),0xDD ( Dead Data ) 填充已被释放的堆内存(例如 delete ),0xFD( deFencde Data ) 初始化受保护的内存(debug 版在动态分配内存的前后加入保护内存以防止越界访问),其中括号中的词是微软建议的助记词。这样做的好处是这些值都很大,作为指针是不可能的(而且 32 位系统中指针很少是奇数值,在有些系统中奇数的指针会产生运行时错误),作为数值也很少遇到,而且这些值也很容易辨认,因此这很有利于在 Debug 版中发现 Release 版才会遇到的错误。要特别注意的是,很多人认为编译器会用 0 来初始化变量,这是错误的(而且这样很不利于查找错误)。 (2) 通过函数指针调用函数时,会通过检查栈指针验证函数调用的匹配性。(防止原形不匹配) (3) 函数返回前检查栈指针,确认未被修改。(防止越界访问和原形不匹配,与第二项合在一起可大致模拟帧指针省略 FPO )
通常 /GZ 选项会造成 Debug 版出错而 Release 版正常的现象,因为 Release 版中未初始化的变量是随机的,这有可能使指针指向一个有效地址而掩盖了非法访问。
除此之外,/Gm /GF 等选项造成错误的情况比较少,而且他们的效果显而易见,比较容易发现。
三、怎样“调试” Release 版的程序
遇到 Debug 成功但 Release 失败,显然是一件很沮丧的事,而且往往无从下手。如果你看了以上的分析,结合错误的具体表现,很快找出了错误,固然很好。但如果一时找不出,以下给出了一些在这种情况下的策略。
1. 前面已经提过,Debug 和 Release 只是一组编译选项的差别,实际上并没有什么定义能区分二者。我们可以修改 Release 版的编译选项来缩小错误范围。如上所述,可以把 Release 的选项逐个改为与之相对的 Debug 选项,如 /MD 改为 /MDd、/O1 改为 /Od,或运行时间优化改为程序大小优化。注意,一次只改一个选项,看改哪个选项时错误消失,再对应该选项相关的错误,针对性地查找。这些选项在 Project\Settings... 中都可以直接通过列表选取,通常不要手动修改。由于以上的分析已相当全面,这个方法是最有效的。
2. 在编程过程中就要时常注意测试 Release 版本,以免最后代码太多,时间又很紧。
3. 在 Debug 版中使用 /W4 警告级别,这样可以从编译器获得最大限度的错误信息,比如 if( i =0 )就会引起 /W4 警告。不要忽略这些警告,通常这是你程序中的 Bug 引起的。但有时 /W4 会带来很多冗余信息,如 未使用的函数参数 警告,而很多消息处理函数都会忽略某些参数。我们可以用
#progma warning(disable: 4702) //禁止
//...
#progma warning(default: 4702) //重新允许
来暂时禁止某个警告,或使用 #progma warning(push, 3) //设置警告级别为 /W3 //… #progma warning(pop) //重设为 /W4 来暂时改变警告级别,有时你可以只在认为可疑的那一部分代码使用 /W4。
4.你也可以像 Debug 一样调试你的 Release 版,只要加入调试符号。在 Project/Settings... 中,选中 Settings for "Win32 Release",选中 C/C++ 标签,Category 选 General,Debug Info 选 Program Database。再在 Link 标签 Project options 最后加上 "/OPT:REF" (引号不要输)。这样调试器就能使用 pdb 文件中的调试符号。但调试时你会发现断点很难设置,变量也很难找到——这些都被优化过了。不过令人庆幸的是,Call Stack 窗口仍然工作正常,即使帧指针被优化,栈信息(特别是返回地址)仍然能找到。这对定位错误很有帮助。
对话框编程技巧
一、如何往基于Dialog的程序添加菜单
[1.1] 先添加菜单(IDR_MENU1)资源,并加上需要的菜单项。 [1.2] 编辑对话框资源IDD_DLGMENUTOOLBAR_DIALOG的属性,在属性对话框中选择IDR_MENU1即可。
[1.3] 假如您不希望在对话框属性中直接设置菜单,而通过代码在程序中动态生成可以采用如下方法:
[1.3.1]在CDlgMenuToolbarDlg类声名中添加成员变量CMenu m_menu 再在CDlgMenuToolbarDlg::OnInitDialog() 中添加如下代码: //加载菜单 m_menu.LoadMenu(IDR_MENU1); //设置当前菜单 SetMenu(&m;_menu); //当你不需要菜单时可以用 SetMenu(NULL);来取消当前菜单 二、如何往基于Dialog的程序添加工具栏
[2.1] 先添加工具栏(IDR_TOOLBAR1)资源,并画好各个按钮。 [2.2] 在CDlgMenuToolbarDlg类声名中添加成员变量 CToolBar m_wndtoolbar; [2.3] 在CDlgMenuToolbarDlg::OnInitDialog() 中添加如下代码 //添加一个平面工具条 if (!m_wndtoolbar.CreateEx( this,TBSTYLE_FLAT , WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS, CRect(4,4,0,0)) || !m_wndtoolbar.LoadToolBar(IDR_TOOLBAR1) ) { TRACE0(“failed to create toolbar\n”); return FALSE; } m_wndtoolbar.ShowWindow(SW_SHOW); RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0); 三、为工具栏添加工具提示
[3.1] 在CDlgMenuToolbarDlg类定义中手工添加消息映射函数的定义,如下黑体部分
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg BOOL OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult);
DECLARE_MESSAGE_MAP()
[3.2] 在CDlgMenuToolbarDlg.cpp添加函数的实现代码 //工具栏提示 BOOL CDlgMenuToolbarDlg::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult) { ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);
// UNICODE消息
TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
//TCHAR szFullText[512];
CString strTipText;
UINT nID = pNMHDR->idFrom;
if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
{
// idFrom为工具条的HWND
nID = ::GetDlgCtrlID((HWND)nID);
}
if (nID != 0) //不为分隔符
{
strTipText.LoadString(nID);
strTipText = strTipText.Mid(strTipText.Find('\n',0)+1);
#ifndef _UNICODE if (pNMHDR->code == TTN_NEEDTEXTA) { lstrcpyn(pTTTA->szText, strTipText, sizeof(pTTTA->szText)); } else { _mbstowcsz(pTTTW->szText, strTipText, sizeof(pTTTW->szText)); } #else if (pNMHDR->code == TTN_NEEDTEXTA) { _wcstombsz(pTTTA->szText, strTipText,sizeof(pTTTA->szText)); } else { lstrcpyn(pTTTW->szText, strTipText, sizeof(pTTTW->szText)); } #endif
*pResult = 0;
// 使工具条提示窗口在最上面
::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,SWP_NOACTIVATE|
SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
return TRUE;
}
return TRUE;
}
[3.3] 在CDlgMenuToolbarDlg.cpp中添加消息映射,请看如下代码中的黑体部分 BEGIN_MESSAGE_MAP(CDlgMenuToolbarDlg, CDialog) //{AFX_MSG_MAP(CDlgMenuToolbarDlg) ON_WM_PAINT() ON_NOTIFY_EX( TTN_NEEDTEXT, 0, OnToolTipText ) //}AFX_MSG_MAP END_MESSAGE_MAP()
四、永远在最前面的对话框 void CMyDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized) { if (nState == WA_INACTIVE) { SetForegroundWindow(); SetWindowPos(&wndTopMost;,0,0,0,0,SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); } }
五、其它 为了使你的程序看起来更酷,还可以在CDlgMenuToolbarDlg::OnPaint()中修改代码实现Dialog 填充颜色。 CPaintDC dc(this); CRect rect; GetClientRect(rect); dc.FillSolidRect(rect, RGB(60,110,170));
VC中使用存储过程
看了很多VC数据库的书,里面很少设计到用VC和存储过程操作的资料,网上找了又找,结果还是很少,收集了一下一些代码片断,以供参考。
[arrow]代码片断1:
_variant_t _vValue; _variant_t RecordsAffected; VARIANT vtRetval; vtRetval.vt = VT_I2; _CommandPtr pCmd = NULL; _ParameterPtr pPrm = NULL; _variant_t vtEmpty(DISP_E_PARAMNOTFOUND, VT_ERROR); _variant_t vtEmpty2(DISP_E_PARAMNOTFOUND, VT_ERROR); CString tempsql; pCmd.CreateInstance( __uuidof( Command ) ); pPrm.CreateInstance( __uuidof( Parameter ) ); pCmd ->ActiveConnection = pConn; pCmd ->CommandType = adCmdStoredProc; pCmd ->CommandText = _bstr_t(L”sp_addumpdevice”); pPrm = pCmd ->CreateParameter( _bstr_t(“retval”),adInteger,adParamReturnValue,sizeof(int),vtRetval); pCmd ->Parameters ->Append( pPrm ); if(strcmp(sblx,”0”)==0) { pPrm = pCmd ->CreateParameter( _bstr_t(“sblx”),adChar,adParamInput,4,”disk” ); pPrm ->Value =”disk”; } else {
pPrm = pCmd ->CreateParameter( _bstr_t("sblx"),adChar,adParamInput,4,"tape");
pPrm ->Value ="tape";
}
pCmd ->Parameters ->Append( pPrm );
pPrm = pCmd ->CreateParameter( _bstr_t("sbmc"),adVarChar,adParamInput,20,_bstr_t(sbmc));
pPrm ->Value =_bstr_t(sbmc);
pCmd ->Parameters ->Append( pPrm );
pPrm = pCmd ->CreateParameter( _bstr_t("bflj"),adVarChar,adParamInput,50,_bstr_t(bflj));
pPrm ->Value =_bstr_t(bflj);
pCmd ->Parameters ->Append( pPrm );
pCmd ->Execute( &vtEmpty;, &vtEmpty2;,adExecuteNoRecords);
[arrow]代码片断2:
m_pParam =m_pCommand->CreateParameter(“id”,adInteger,adParamInput,-1,(_variant_t)”10”); //给参数设置各属性 m_pCommand->Parameters->Append(m_pParam);//加入到Command对象的参数集属性中
m_pParam1=m_pCommand->CreateParameter(“Name”,adVarChar,adParamInput,20,(_variant_t)”songwenfeng”); m_pCommand->Parameters->Append(m_pParam1);
m_pParam2=m_pCommand->CreateParameter(“sdate”,adVarChar,adParamInput,32,(_variant_t)”2004-6-8”); m_pCommand->Parameters->Append(m_pParam2);
[arrow]代码片断3:
_ParameterPtr para[3]; _CommandPtr pCmd;
pCmd.CreateInstance("ADODB.Command");
para[0].CreateInstance("ADODB.Parameter");
para[1].CreateInstance("ADODB.Parameter");
para[2].CreateInstance("ADODB.Parameter");
pCmd->ActiveConnection=m_pConn;
pCmd->CommandText="存储过程名"
para[0]=pCmd->CreateParameter("", adBSTR,adParamInput,
sizeof(char[50]),vVar); //字符串型输入参数
pCmd->Parameters->Append(para[0]);
para[1]=pCmd->CreateParameter("", adInteger,adParamInput,
sizeof(int),olevariantVar); //整型输入参数
pCmd->Parameters->Append(para[1]);
para[2]=pCmd->CreateParameter("", adBSTR,adParamOutput,
sizeof(char[50]),""); //字符串型输出参数
pCmd->Parameters->Append(para[2]);
pCmd->Execute( NULL, NULL, adCmdStoredProc);
[arrow]代码片断4:
使用vc++ ,sql server。 假设一个表:name sex age. 现在想求出有多少个男性,多少个女性。 如果不是用存储过程,就必须一条一条的比较。 现在想问一下:如何写这个存储过程,如何在vc中调用这个存储过程,如何取得存储过程的结果。
_CommandPtr cmmd; _ParameterPtr param; HRESULT hr = cmmd.CreateInstance(__uuidof(Command)); if(FAILED(hr)) { AfxMessageBox(“NewNetDatabase()中创建_CommandPtr对象失败”); return “创建对象失败”; } cmmd->ActiveConnection = m_pConnection;//需要使用的ADO连接 cmmd->CommandType=adCmdStoredProc; cmmd->CommandText=_bstr_t(“pkg_shenrole.AddRoleInfo”);//这个就是存储过程名称 param = cmmd->CreateParameter(“”,adBigInt, adParamInput, sizeof(long),(long)(nRoleId)); cmmd->Parameters->Append(param);
利用HTTP方式上传
#include #include #include #include BOOL UseHttpSendReqEx(HINTERNET hRequest, DWORD dwPostSize,CString strLocalFile); BOOL Upload(CString bstrLocalFile,CString bstrServerIP,CString strServerPort,CString bstrRemoteFile); #define BUFFSIZE 500
void main( int argc, char **argv ) {
if (argc < 5)
{
printf("Usage: Bigpost \n");
printf(" is the local file to POST\n");
printf(" is the server's IP to POST to\n");
printf(" is the server's Port to POST to\n");
printf(" is the virtual path to POST to\n");
exit(0);
}
Upload(argv[1],argv[2],argv[3],argv[4]);
} BOOL UseHttpSendReqEx(HINTERNET hRequest, DWORD dwPostSize,CString strLocalFile) { DWORD dwRead; BYTE* buffer; printf(“Local file:%s\n”,strLocalFile); FILE* fLocal; if((fLocal=fopen(strLocalFile,”rb”))==NULL){ printf(“Can’t open the file:%s,maybe it doesn’t exist!\n”,strLocalFile); return false; } fseek(fLocal,0L,SEEK_END); dwRead=ftell(fLocal); rewind(fLocal); buffer=(BYTE *)malloc(dwRead); if(!buffer){ printf(“not enough memory!\n”); return false; } printf(“length of file:%d\n”,dwRead); dwRead=fread(buffer,1,dwRead,fLocal); dwPostSize=dwRead;
INTERNET_BUFFERS BufferIn;
DWORD dwBytesWritten;
BOOL bRet;
BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or error will occur
BufferIn.Next = NULL;
BufferIn.lpcszHeader = NULL;
BufferIn.dwHeadersLength = 0;
BufferIn.dwHeadersTotal = 0;
BufferIn.lpvBuffer = NULL;
BufferIn.dwBufferLength = 0;
BufferIn.dwBufferTotal = dwPostSize; // This is the only member used other than dwStructSize
BufferIn.dwOffsetLow = 0;
BufferIn.dwOffsetHigh = 0;
if(!HttpSendRequestEx( hRequest, &BufferIn;, NULL, 0, 0))
{
printf( "Error on HttpSendRequestEx %d\n",GetLastError() );
return FALSE;
}
bRet=TRUE;
if(bRet=InternetWriteFile( hRequest, buffer, dwPostSize, &dwBytesWritten;))
printf( "\r%d bytes sent.", dwPostSize);
if(!bRet)
{
printf( "\nError on InternetWriteFile %lu\n",GetLastError() );
return FALSE;
}
if(!HttpEndRequest(hRequest, NULL, 0, 0))
{
printf( "Error on HttpEndRequest %lu \n", GetLastError());
return FALSE;
}
fclose(fLocal);
free(buffer);
return TRUE;
}
BOOL Upload(CString strLocalFile,CString strServerIP,CString strServerPort,CString strRemoteFile){ DWORD dwPostSize=0; int intServerPort=atoi(strServerPort); HINTERNET hSession = InternetOpen( “HttpSendRequestEx”, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if(!hSession) { printf(“Failed to open session\n”); return FALSE; } HINTERNET hConnect = InternetConnect(hSession, strServerIP, intServerPort, NULL, NULL, INTERNET_SERVICE_HTTP,NULL, NULL); if (!hConnect){ printf( “Failed to connect\n” ); return FALSE; }else{ HINTERNET hRequest = HttpOpenRequest(hConnect, “PUT”, strRemoteFile, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0); if (!hRequest){ printf( “Failed to open request handle\n” ); }else{ if(UseHttpSendReqEx(hRequest, dwPostSize,strLocalFile)) { char pcBuffer[BUFFSIZE]; DWORD dwBytesRead;
printf("\nThe following was returned by the server:\n");
do
{ dwBytesRead=0;
if(InternetReadFile(hRequest, pcBuffer, BUFFSIZE-1, &dwBytesRead;))
{
pcBuffer[dwBytesRead]=0x00; // Null-terminate buffer
printf("%s", pcBuffer);
}
else
printf("\nInternetReadFile failed");
}while(dwBytesRead>0);
printf("\n");
}
if (!InternetCloseHandle(hRequest))
printf( "Failed to close Request handle\n" );
}
if(!InternetCloseHandle(hConnect))
printf("Failed to close Connect handle\n");
}
if( InternetCloseHandle( hSession ) == FALSE ){
printf( "Failed to close Session handle\n" );
return FALSE;
}
printf( "\nFinished.\n" );
return TRUE;
}
获取系统有几个逻辑分区
char cr[3]; char lable[12]; for(int n=0;n<=25;n++) { CString str; str.Format(“%c:”,n+’A’); strcpy(cr,str); int dt = GetDriveType(cr); int succeed = GetVolumeInformation(cr,lable,12,NULL,NULL,NULL,NULL,0); switch(dt) { case DRIVE_UNKNOWN: { str.Format(“(%s) %s is DRIVE_UNKNOWN!”,lable,cr); break; } case DRIVE_REMOVABLE: { if(succeed) { str.Format(“(%s) %s is DRIVE_REMOVABLE!”,lable,cr); } else { str.Format(“%s is DRIVE_REMOVABLE!”,cr); } break; } case DRIVE_FIXED: { str.Format(“(%s) %s is DRIVE_FIXED!”,lable,cr); break; } case DRIVE_REMOTE: { str.Format(“(%s) %s is DRIVE_REMOTE!”,lable,cr); break; } case DRIVE_CDROM: { str.Format(“(%s) %s is DRIVE_CDROM!”,lable,cr); break; } case DRIVE_RAMDISK: { str.Format(“(%s) %s is DRIVE_RAMDISK!”,lable,cr); break; } default: { str.Empty(); break; } } if(!str.IsEmpty()) { AfxMessageBox(str); } }
得到指定文件的信息
char* szFileName = “C:\EnochShen.exe”; DWORD dwSize = GetFileVersionInfoSize(szFileName,NULL); LPVOID pBlock = malloc(dwSize); GetFileVersionInfo(szFileName,0,dwSize,pBlock);
char* pVerValue = NULL;
UINT nSize = 0;
VerQueryValue(pBlock,TEXT("\\VarFileInfo\\Translation"),
(LPVOID*)&pVerValue;,&nSize;);
CString strSubBlock,strTranslation,strTemp;
strTemp.Format("000%x",*((unsigned short int *)pVerValue));
strTranslation = strTemp.Right(4);
strTemp.Format("000%x",*((unsigned short int *)&pVerValue;[2]));
strTranslation += strTemp.Right(4);
//080404b0为中文,040904E4为英文
//文件描述
strSubBlock.Format("\\StringFileInfo\\%s\\FileDescription",strTranslation);
VerQueryValue(pBlock,strSubBlock.GetBufferSetLength(256),(LPVOID*)&pVerValue;,&nSize;);
strSubBlock.ReleaseBuffer();
strTemp.Format("文件描述: %s",pVerValue);
AfxMessageBox(strTemp);
//内部名称
strSubBlock.Format("\\StringFileInfo\\%s\\InternalName",strTranslation);
VerQueryValue(pBlock,strSubBlock.GetBufferSetLength(256),(LPVOID*)&pVerValue;,&nSize;);
strSubBlock.ReleaseBuffer();
strTemp.Format("文件描述: %s",pVerValue);
AfxMessageBox(strTemp);
//合法版权
strSubBlock.Format("\\StringFileInfo\\%s\\LegalTradeMarks",strTranslation);
VerQueryValue(pBlock,strSubBlock.GetBufferSetLength(256),(LPVOID*)&pVerValue;,&nSize;);
strSubBlock.ReleaseBuffer();
strTemp.Format("合法版权: %s",pVerValue);
AfxMessageBox(strTemp);
//原始文件名
strSubBlock.Format("\\StringFileInfo\\%s\\OriginalFileName",strTranslation);
VerQueryValue(pBlock,strSubBlock.GetBufferSetLength(256),(LPVOID*)&pVerValue;,&nSize;);
strSubBlock.ReleaseBuffer();
strTemp.Format("原始文件名: %s",pVerValue);
AfxMessageBox(strTemp);
//产品名称
strSubBlock.Format("\\StringFileInfo\\%s\\ProductName",strTranslation);
VerQueryValue(pBlock,strSubBlock.GetBufferSetLength(256),(LPVOID*)&pVerValue;,&nSize;);
strSubBlock.ReleaseBuffer();
strTemp.Format("产品名称: %s",pVerValue);
AfxMessageBox(strTemp);
//产品版本
strSubBlock.Format("\\StringFileInfo\\%s\\ProductVersion",strTranslation);
VerQueryValue(pBlock,strSubBlock.GetBufferSetLength(256),(LPVOID*)&pVerValue;,&nSize;);
strSubBlock.ReleaseBuffer();
strTemp.Format("产品版本: %s",pVerValue);
AfxMessageBox(strTemp);
free(pBlock);
修改注册表开机自动启动程序
HKEY hKey; char szFileName[256]; GetModuleFileName(NULL,szFileName,256); RegOpenKey(HKEY_LOCAL_MACHINE,”SOFTWARE\Microsoft\windows\currentversion\run”,&hKey;); if(m_bAutoRun) { RegSetValueEx(hKey,”RunmeAtStartup”,0,REG_SZ,(BYTE *)szFileName,sizeof(szFileName)); } else { RegDeleteValue(hKey,”RunmeAtStartup”); } RegCloseKey(hKey);