huahit

常用链接

统计

积分与排名

有用的编程连接

最新评论

VC环境下对函数调用的汇编分析


作者: pupa 发表日期: 2006-02-15 13:50 文章属性: 转载 复制链接


VC环境下对函数调用的汇编分析【原创】

前沿:对于我们平常编程中常出现一些细节,如__stdcall和__cdecl编译器如何为我们处理,函数中变量以及new出来的变量到底存放于哪些地方,等等一些列问题。本文将和大家一起分析程序执行的汇编语言,通过对此过程掌握使自己在开发中熟悉并优化自己的代码。作者:天衣有缝,联系邮件:waterpub@mail.csdn.net,MSN:waterpub_cn@hotmail.com,我的QQ群3226292,转载请保留完整文档。

1.环境:我使用的开发环境是vc7.1,其release单步调试需要对项目属性作如下修改:
“C++”--》“常规”--》“调试信息格式” 改为:“用于“编辑并继续”的程序数据库(/ZI)”
“C++”--》“优化”--》“优化”       改为:禁用(/Od)
如果你是vc6环境,可如下修改release版属性:
选中Win32 Release然后
Project-》setting-》C/C++ -》Category-》General
-》Optimization-》Disable(Debug)
-》Debug Info-》Program DataBase
-》Link---》Generate Debug Info打上钩

2.c语言代码,如下:
/***开始*****************************************************/
#include "stdafx.h"
int __cdecl add(int a, int b)
{
int c;
c = a + b;
return c;
}
int _tmain(int argc, _TCHAR* argv[])
{
int iResult = add(123, 456);
printf("\n************\n");

return 0;
}
/***结束*****************************************************/

3.程序分析过程,F10单步启动程序:
/***开始*****************************************************/
int _tmain(int argc, _TCHAR* argv[])
{
00401020 push     ebp         建立堆栈帧
00401021 mov       ebp,esp       存入栈基地址,运行后EBP = 0012FEE4
                          (表明默认堆栈大小约为1兆)
00401023 sub       esp,44h       空出一块堆栈区,不知道是干什么的,可有高人指点?
×××××××××××××××××××××
在一篇vc6的汇编调试文章中看到main中变量存储于此堆栈中,但是我在vc7调试发现iresult地址根本不在堆栈范围之内,不知作何解释
×××××××××××××××××××××
00401026 push     ebx         保护现场
00401027 push     esi
00401028 push     edi
int iResult = add(123, 456);
00401029 push     1C8h         参数入栈
0040102E push     7Bh
00401030 call     add (401000h)   执行函数调用,按F11跳到本文蓝色处
×××××××××××××××××××××
call指令详解:
call指令是push和jmp的结合,先执行push eip将当前地址入栈(函数调用完毕需要用这个地址返回),然后调用jmp指令。因为普通指令无法对eip操作,所以在很多病毒程序中常有如下语句:
    call         @@get_eip
@@get_eip:
    pop ebp ;取得eip
×××××××××××××××××××××
00401035 add       esp,8         释放123和456变量所占堆栈
00401038 mov       dword ptr [iResult],eax       从eax取出计算结果
printf("\n************\n");       下面是一个函数的测试
0040103B push     offset string "\n************\n" (4060FCh)     变量地址入栈
00401040 call     printf (401051h) 执行call调用函数
00401045 add       esp,4         变量地址出栈

return 0;
00401048 xor       eax,eax       使eax为0,eax就是返回给操作系统的值
}
0040104A pop       edi
0040104B pop       esi
0040104C pop       ebx         恢复现场
0040104D mov       esp,ebp       平衡堆栈
0040104F pop       ebp         释放堆栈帧
00401050 ret                 返回操作系统调用处

函数定义:
int __cdecl add(int a, int b)
{
00401000 push     ebp       建立堆栈帧
00401001 mov       ebp,esp   存入栈基地址
00401003 sub       esp,44h   开辟变量使用的堆栈区,供函数内部变量使用
                      执行前ESP = 0012FE84,执行后ESP = 0012FE40
×××××××××××××××××××××
此处可以打开内存0x0012FE8C,看到 7b 00 00 00 c8 01 00 00,这就是我们传入的123(0x0012fe8c处)和456(0x0012fe90处)变量
×××××××××××××××××××××
00401006 push     ebx       保护现场
00401007 push     esi
00401008 push     edi
int c;
c = a + b;
00401009 mov       eax,dword ptr [a]   第一个参数,也就是[ebp+8]
0040100C add       eax,dword ptr   第二个参数,也就是[ebp+c]

0040100F mov       dword ptr [c],eax   c变量在栈中,地址为0x0012fe80,就是变量堆栈区顶部
return c;
00401012 mov       eax,dword ptr [c]   计算结果存入eax
}
00401015 pop       edi     回复现场
00401016 pop       esi
00401017 pop       ebx
00401018 mov       esp,ebp   平衡堆栈,回收变量堆栈区
0040101A pop       ebp     释放堆栈帧
0040101B ret             回到调用地址,读者从这里转到粉红色处接着看
/***结束*****************************************************/


4.关于__cdecl和__stdcall:(vc项目默认调用方式为__cdecl)
我们将上面的add函数改为__stdcall形式,执行过程如下,我在和上面__cdecl调用不同的地方将作出标记:
/***开始*****************************************************/
int _tmain(int argc, _TCHAR* argv[])
{
00401020 push     ebp
00401021 mov       ebp,esp
00401023 sub       esp,44h
00401026 push     ebx
00401027 push     esi
00401028 push     edi
int iResult = add(123, 456);
00401029 push     1C8h
0040102E push     7Bh
00401030 call     add (401000h)
注意,这句话后面没有了add esp,8,原因:__stdcall调用方式的入栈参数在函数内部已经释放了,所以这句话也就不需要了。
00401035 mov       dword ptr [iResult],eax
printf("\n************\n");
00401038 push     offset string "\n************\n" (4060FCh)
0040103D call     printf (40104Eh)
00401042 add       esp,4

return 0;
00401045 xor       eax,eax
}
00401047 pop       edi
00401048 pop       esi
00401049 pop       ebx
0040104A mov       esp,ebp
0040104C pop       ebp
0040104D ret    

函数部分:

int __stdcall add(int a, int b)
{
00401000 push     ebp
00401001 mov       ebp,esp
00401003 sub       esp,44h
00401006 push     ebx
00401007 push     esi
00401008 push     edi
int c;
c = a + b;
00401009 mov       eax,dword ptr [a]
0040100C add       eax,dword ptr
0040100F mov       dword ptr [c],eax
return c;
00401012 mov       eax,dword ptr [c]
}
00401015 pop       edi
00401016 pop       esi
00401017 pop       ebx
00401018 mov       esp,ebp
0040101A pop       ebp
0040101B ret       8  
这句话就是__stdcall调用方式的入栈参数
在函数内部释放的语句!
/***结束*****************************************************/
说明:对于函数的传值还是传址,大家在此之后自行分析,相信初学者可以看到很多细节方面的咚咚

5.全局变量的初始化:
#include "stdafx.h"
const char szName[]= "http://blog.csdn.net/waterpub";
class CTestClass
{
public:
CTestClass()
{
printf("CTestClass::CTestClass()\n");
}
~CTestClass()
{
printf("CTestClass::~CTestClass()\n");
}
};
const CTestClass tobject;
int _tmain(int argc, _TCHAR* argv[])
{
printf("\n************\n");
return 0;
}

在这个程序中szname如何初始化的,为何没有执行到对应的反汇编语句?
因为main函数开始执行的时候,szname已经初始化了,所有我们运行不到这个地方。当用户启动这个exe程序的时候,进入C/C++ 运行时库代码(CRTStartup),由它初始化静态变量及全局变量,然后再转入main函数。

我们现在在上面绿色部分设置一个断点,然后运行,程序断在此处。按(Ctrl+Alt+C :VC7.1的快捷键,vc6有相应的菜单项),点到最下面调用函数,从此可以看出:程序启动时由操作系统执行“mainCRTStartup”函数,简化的代码我贴在下面了,一些很直观的英文没有翻译:
/***开始*****************************************************/
int WinMainCRTStartup(void)

{
int initret;
int mainret;
OSVERSIONINFOA *posvi;
int managedapp;
posvi = (OSVERSIONINFOA *)_alloca(sizeof(OSVERSIONINFOA));//用这个函数避免了全局存储缓冲区的分配运行时检测

//操作系统版本相关的一些代码
posvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
(void)GetVersionExA(posvi);
_osplatform = posvi->dwPlatformId;
_winmajor = posvi->dwMajorVersion;
_winminor = posvi->dwMinorVersion;
_osver = (posvi->dwBuildNumber) & 0x07fff;
if ( _osplatform != VER_PLATFORM_WIN32_NT )
_osver |= 0x08000;
_winver = (_winmajor << 8) + _winminor;
//是否为托管程序
managedapp = check_managed_app();

#ifdef _MT
if ( !_heap_init(1) )           /* 多线程用此方式初始化堆 */
#else /* _MT */
if ( !_heap_init(0) )           /* 单线程用此方式初始化堆 */
#endif /* _MT */
fast_error_exit(_RT_HEAPINIT); /* write message and die */

#ifdef _MT
if( !_mtinit() )             /* initialize multi-thread */
fast_error_exit(_RT_THREAD);   /* write message and die */
#endif /* _MT */

/*
* Initialize the Runtime Checks stuff
*/
#ifdef _RTC
_RTC_Initialize();             // 初始化runtime
#endif /* _RTC */
/*
* 下面是剩余的初始化代码(包括我的程序的全局变量的初始化就在这里了,呵呵)
* 初始化完毕调用 main 或 WinMain(在try里面执行的)
*/

__try {

if ( _ioinit() < 0 )         /* io初始化,应该是输入输出吧,不太清楚 */
  _amsg_exit(_RT_LOWIOINIT);

#ifdef WPRFLAG
/* get wide cmd line info */
_wcmdln = (wchar_t *)__crtGetCommandLineW();       //取得运行参数的字符串

/* get wide environ info */
_wenvptr = (wchar_t *)__crtGetEnvironmentStringsW();   //取得环境变量的字符串

if ( _wsetargv() < 0 )
  _amsg_exit(_RT_SPACEARG);
if ( _wsetenvp() < 0 )
  _amsg_exit(_RT_SPACEENV);
#else /* WPRFLAG */
/* get cmd line info */
_acmdln = (char *)GetCommandLineA();

/* get environ info */
_aenvptr = (char *)__crtGetEnvironmentStringsA();

if ( _setargv() < 0 )
  _amsg_exit(_RT_SPACEARG);
if ( _setenvp() < 0 )
  _amsg_exit(_RT_SPACEENV);
#endif /* WPRFLAG */

initret = _cinit(TRUE);             /* 全局变量初始化,找的就是这里!!! */
if (initret != 0)
  _amsg_exit(initret);

#ifdef _WINMAIN_

StartupInfo.dwFlags = 0;
GetStartupInfo( &StartupInfo );

#ifdef WPRFLAG
lpszCommandLine = _wwincmdln();
mainret = wWinMain(
#else /* WPRFLAG */
lpszCommandLine = _wincmdln();
mainret = WinMain(
#endif /* WPRFLAG */
  GetModuleHandleA(NULL),
  NULL,
  lpszCommandLine,
  StartupInfo.dwFlags & STARTF_USESHOWWINDOW
  ? StartupInfo.wShowWindow
  : SW_SHOWDEFAULT
  );
#else /* _WINMAIN_ */

#ifdef WPRFLAG
__winitenv = _wenviron;
mainret = wmain(__argc, __wargv, _wenviron);
#else /* WPRFLAG */
__initenv = _environ;
mainret = main(__argc, __argv, _environ);    
// 就在这里调用了main,因为运行时代码在exe文件中,所以可以把main函数拿来调用(跟普通的函数没什么区别了,如果你看了win32汇编就知道main或winmain名字都不是定死的了)!
#endif /* WPRFLAG */

#endif /* _WINMAIN_ */

if ( !managedapp )
  exit(mainret);

_cexit();

}
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )   // 异常就到这里来了,比如丢失了dll文件
{
/*
* Should never reach here
*/

mainret = GetExceptionCode();

if ( !managedapp )
  _exit(mainret);

_c_exit();

} /* end of try - except */

return mainret;
}
/***结束*****************************************************/


6.win32的启动过程
既然console的程序我们分析出来了,win32的又有什么区别呢?区别还是有的(启动程序的核心代码都在crt0.c文件中),上面我把具体的分析方法阐述了一下,win32的分析就留给大家做好啦,:)

7.错误可能也有的,或者可以写的更好,但本菜也只有这个水平了,贻笑大方,悉请高手不吝指正。文章可能随时修改,如果你有什么问题或好的想法,到我的blog(http://blog.csdn.net/waterpub)上留言,不要在这里留了,我来的少,:)

8.深圳南山科技园科技工业大厦     2005-02-22 17:00:00



















CNBIE BLOG
--------------------------------------------------------------------------------

VC基于ActiveX组件的浏览器插件
原文:VC基于ActiveX组件的浏览器插件

插件是在不久的将来使你的浏览器不再平淡无奇的部分原因。你可以在浏览器设置时用插件加进定制的功能。大多数情况下,你会发现插件是免费的,而对插件提供的服务却不便宜。这对于程序员意味着什么呢?这就意味着,如果决定在自己的Web 站点加进某种特殊的功能,那么你得为其支付服务费。有时候,你可以先付一笔初始服务费,然后每年交一笔维持费用。从用户的观点来看,这意味着他们通常可以免费增强浏览器的功能,获得一个特定的因特网站点提供的附加功能。
  本节列举了四种ActiveX插件,这些插件在现在或不久的将来会被见到。 在因特网站点上使用这些插件时,需要为其提供服务支持,我们总是假定购买这些服务是要花费一定代价的。与所列的厂商地址联系,以得到价格信息。
  注释 某些插件取得了如此巨大的成功,以至于它们成了IE4.0 的一部分或者将被嵌入Windows 98。
  VDOLive/VDOLive Tools/VDOPhone
  因特网:http://www.clubvdo.net/clubvdo/Default.asp
  VDOLive的基本目标是让你在自己的浏览器上播放位于Web站点上的声音影像资料,它包括播放电影以及其它类型的动画的功能。新的VDOLive2.0版保证了更好的影视质量(每秒钟10-15帧)和影像大小(352x288)。它还包括一个有趣的想法:故事书模式。在这种模式下,影像仍然与声音同步。实际上,你将看到情节逐渐展开,这一点就像在故事书中看到的那样。
  如果打算创建一个庞大的或者复杂的Web站点,你会想到购买 VDOLiveTools。VDOLive Tools包括压缩影视功能以及使Web站点界面友好所需的一切。它所用的AVI 格式与你以前在任何场合曾用过的AVI格式一样, 也就是说着你可以将其与其它编程工具一起使用,亦即在任何可能使用AVI文件的地方,均可使用该产品。
  VDOPhone被设计为双向会谈产品,它通过浏览器工作,而不是一个单独使用的产品,这种灵活性意味着可以设计一个Web页面,在其中嵌入许多不同的功能, 包括定制的ActiveX组件。即便VDOPhone不像市场上有的产品那样功能齐全, 但它被设计成可与微软的网络会议软件一起工作。你可以实际召开一个网络会议,在会议期间,如果需要的话,可以提供白板这样的功能。VDOPhone完全依赖于流技术,这意味着,要应用该产品需要强大的硬件支持以得到优异的图像质量。当对方机器中没有安装影视捕获卡时,也可以将VDOPhone以只传送声音的模式使用。
  Acrobat
  http://www.adobe.com/supportservice/custsupport/LIBRARY/44ae.htm
  Adobe的Acrobat Reader(阅读器)已经应用于多种场合。如果你是一位程序员,很可能某个时候在自己的机器上已经使用过Acrobat Reader, 该阅读器以书本的形式阅读帮助文件。Acrobat使用PDF(可携带文档格式)格式以显示设备支持的方式显示信息。 新的ActiveX 版本允许你用自己的浏览器观看 PDF文件, 而不是下载文件并且启动整个Acrobat reader。



CNBIE BLOG
--------------------------------------------------------------------------------

VC开发数据库基础之ADO篇 (1)
原文:VC开发数据库基础之ADO篇 (1)

VC开发数据库基础之ADO篇
一、ADO简介
ADO(ActiveX Data Object)是Microsoft数据库应用程序开发的新接口,是建立在OLE DB之上的高层数据库访问技术,请不必为此担心,即使你对OLE DB,COM不了解也能轻松对付ADO,因为它非常简单易用,甚至比你以往所接触的ODBC API、DAO、RDO都要容易使用,并不失灵活性。本文将详细地介绍在VC下如何使用ADO来进行数据库应用程序开发,并给出示例代码。
本文示例代码

二、基本流程
万事开头难,任何一种新技术对于初学者来说最重要的还是“入门”,掌握其要点。让我们来看看ADO数据库开发的基本流程吧!
(1)初始化COM库,引入ADO库定义文件
(2)用Connection对象连接数据库
(3)利用建立好的连接,通过Connection、Command对象执行SQL命令,或利用Recordset对象取得结果记录集进行查询、处理。
(4)使用完毕后关闭连接释放对象。

准备工作:
为了大家都能测试本文提供的例子,我们采用Access数据库,您也可以直接在我们提供的示例代码中找到这个test.mdb。
下面我们将详细介绍上述步骤并给出相关代码。
【1】COM库的初始化
我们可以使用AfxOleInit()来初始化COM库,这项工作通常在CWinApp::InitInstance()的重载函数中完成,请看如下代码:


BOOL CADOTest1App::InitInstance()
{
AfxOleInit();
......

【2】用#import指令引入ADO类型库
我们在stdafx.h中加入如下语句:(stdafx.h这个文件哪里可以找到?你可以在FileView中的Header Files里找到)
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
这一语句有何作用呢?其最终作用同我们熟悉的#include类似,编译的时候系统会为我们生成msado15.tlh,ado15.tli两个C++头文件来定义ADO库。

几点说明:
(1) 您的环境中msado15.dll不一定在这个目录下,请按实际情况修改
(2) 在编译的时候肯能会出现如下警告,对此微软在MSDN中作了说明,并建议我们不要理会这个警告。
msado15.tlh(405) : warning C4146: unary minus operator applied to unsigned type, result still unsigned

【3】创建Connection对象并连接数据库
首先我们需要添加一个指向Connection对象的指针:
_ConnectionPtr m_pConnection;
下面的代码演示了如何创建Connection对象实例及如何连接数据库并进行异常捕捉。


BOOL CADOTest1Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
HRESULT hr;
try
{
hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象
if(SUCCEEDED(hr))
{
hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb","","",adModeUnknown);///连接数据库
///上面一句中连接字串中的Provider是针对ACCESS2000环境的,对于ACCESS97,需要改为:Provider=Microsoft.Jet.OLEDB.3.51; }
}
catch(_com_error e)///捕捉异常
{
CString errormessage;
errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);///显示错误信息
}

在这段代码中我们是通过Connection对象的Open方法来进行连接数据库的,下面是该方法的原型
HRESULT Connection15::Open ( _bstr_t ConnectionString, _bstr_t UserID, _bstr_t Password, long Options )
ConnectionString为连接字串,UserID是用户名, Password是登陆密码,Options是连接选项,用于指定Connection对象对数据的更新许可权,
Options可以是如下几个常量:
adModeUnknown:缺省。当前的许可权未设置
adModeRead:只读
adModeWrite:只写
adModeReadWrite:可以读写
adModeShareDenyRead:阻止其它Connection对象以读权限打开连接
adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接
adModeShareExclusive:阻止其它Connection对象打开连接
adModeShareDenyNone:允许其它程序或对象以任何权限建立连接

我们给出一些常用的连接方式供大家参考:
(1)通过JET数据库引擎对ACCESS2000数据库的连接

m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\test.mdb","","",adModeUnknown);

(2)通过DSN数据源对任何支持ODBC的数据库进行连接:
m_pConnection->Open("Data Source=adotest;UID=sa;PWD=;","","",adModeUnknown);

(3)不通过DSN对SQL SERVER数据库进行连接: m_pConnection->Open("driver={SQL Server};Server=127.0.0.1;DATABASE=vckbase;UID=sa;PWD=139","","",adModeUnknown);

其中Server是SQL服务器的名称,DATABASE是库的名称

Connection对象除Open方法外还有许多方法,我们先介绍Connection对象中两个有用的属性ConnectionTimeOut与State
ConnectionTimeOut用来设置连接的超时时间,需要在Open之前调用,例如: m_pConnection->ConnectionTimeout = 5;///设置超时时间为5秒
m_pConnection->Open("Data Source=adotest;","","",adModeUnknown);


State属性指明当前Connection对象的状态,0表示关闭,1表示已经打开,我们可以通过读取这个属性来作相应的处理,例如:
if(m_pConnection->State)
m_pConnection->Close(); ///如果已经打开了连接则关闭它


【4】执行SQL命令并取得结果记录集
为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr m_pRecordset;
并为其创建Recordset对象的实例: m_pRecordset.CreateInstance("ADODB.Recordset");
SQL命令的执行可以采用多种形式,下面我们一进行阐述。

(1)利用Connection对象的Execute方法执行SQL命令
Execute方法的原型如下所示:
_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options ) 其中CommandText是命令字串,通常是SQL命令。参数RecordsAffected是操作完成后所影响的行数, 参数Options表示CommandText中内容的类型,Options可以取如下值之一:
adCmdText:表明CommandText是文本命令
adCmdTable:表明CommandText是一个表名
adCmdProc:表明CommandText是一个存储过程
adCmdUnknown:未知





CNBIE BLOG
--------------------------------------------------------------------------------

VC开发小技巧20个
原文:VC开发小技巧20个

VC开发小技巧20个



一、打开CD-ROM
    mciSendString("Set cdAudio door open wait",NULL,0,NULL);



二、关闭CD_ROM
    mciSendString("Set cdAudio door closed wait",NULL,0,NULL);



三、关闭计算机
OSVERSIONINFO OsVersionInfo;   //包含操作系统版本信息的数据结构
OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&OsVersionInfo);   //获取操作系统版本信息
if(OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
{
//Windows98,调用ExitWindowsEx()函数重新启动计算机
DWORD dwReserved;
ExitWindowsEx(EWX_REBOOT,dwReserved); //可以改变第一个参数,实现注销用户、
                          //关机、关闭电源等操作

        // 退出前的一些处理程序

    }



四、重启计算机
    typedef int (CALLBACK *SHUTDOWNDLG)(int);   //显示关机对话框函数的指针
    HINSTANCE hInst = LoadLibrary("shell32.dll"); //装入shell32.dll
    SHUTDOWNDLG ShutDownDialog; //指向shell32.dll库中显示关机对话框函数的指针
    if(hInst != NULL)
    {
        //获得函数的地址并调用之
        ShutDownDialog = (SHUTDOWNDLG)GetProcAddress(hInst,(LPSTR)60);
        (*ShutDownDialog)(0);
    }

五、枚举所有字体
      LOGFONT lf;

    lf.lfCharSet = DEFAULT_CHARSET;     // Initialize the LOGFONT structure

    strcpy(lf.lfFaceName,"");

    CClientDC dc (this);

    // Enumerate the font families

    ::EnumFontFamiliesEx((HDC) dc,&lf,

        (FONTENUMPROC) EnumFontFamProc,(LPARAM) this,0);
  //枚举函数

int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf,

        LPNEWTEXTMETRIC lpntm,DWORD nFontType,long lparam)

{

    // Create a pointer to the dialog window

    CDay7Dlg* pWnd = (CDay7Dlg*) lparam;

    // add the font name to the list box

    pWnd ->m_ctlFontList.AddString(lpelf ->elfLogFont.lfFaceName);

    // Return 1 to continue font enumeration

    return 1;

}
其中m_ctlFontList是一个列表控件变量



六、一次只运行一个程序实例,如果已运行则退出

if( FindWindow(NULL,"程序标题")) exit(0);



七、得到当前鼠标所在位置

    CPoint pt;

    GetCursorPos(&pt); //得到位置



八、上下文菜单事件触发事件:OnContextMenu事件



九、显示和隐藏程序菜单

CWnd *pWnd=AfxGetMainWnd();
if(b_m)   //隐藏菜单
{
pWnd->SetMenu(NULL);
pWnd->DrawMenuBar();
b_m=false;
}
else
{
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME); ////显示菜单   也可改变菜单项
pWnd->SetMenu(&menu);
pWnd->DrawMenuBar();
b_m=true;
menu.Detach();
}



十、获取可执行文件的图标

  HICON hIcon=::ExtractIcon(AfxGetInstanceHandle(),_T("NotePad.exe"),0);

  if (hIcon &&hIcon!=(HICON)-1)

  {

      pDC->DrawIcon(10,10,hIcon);

  }

  DestroyIcon(hIcon);

十一、窗口自动靠边程序演示

BOOL AdjustPos(CRect* lpRect)

{//自动靠边

  int iSX=GetSystemMetrics(SM_CXFULLSCREEN);

  int iSY=GetSystemMetrics(SM_CYFULLSCREEN);

  RECT rWorkArea;

BOOL bResult = SystemParametersInfo(SPI_GETWORKAREA,                       sizeof(RECT),     &rWorkArea, 0);  

  CRect rcWA;

  if(!bResult)

  {//如果调用不成功就利用GetSystemMetrics获取屏幕面积

      rcWA=CRect(0,0,iSX,iSY);

  }

  else

      rcWA=rWorkArea;



  int iX=lpRect->left;

  int iY=lpRect->top;



  if(iX < rcWA.left + DETASTEP && iX!=rcWA.left)

  {//调整左

      //pWnd->SetWindowPos(NULL,rcWA.left,iY,0,0,SWP_NOSIZE);

      lpRect->OffsetRect(rcWA.left-iX,0);

      AdjustPos(lpRect);

      return TRUE;

  }

  if(iY < rcWA.top + DETASTEP && iY!=rcWA.top)

  {//调整上

      //pWnd->SetWindowPos(NULL ,iX,rcWA.top,0,0,SWP_NOSIZE);

      lpRect->OffsetRect(0,rcWA.top-iY);

      AdjustPos(lpRect);

      return TRUE;

  }

  if(iX + lpRect->Width() > rcWA.right - DETASTEP && iX !=rcWA.right-lpRect->Width())

  {//调整右

      //pWnd->SetWindowPos(NULL ,rcWA.right-rcW.Width(),iY,0,0,SWP_NOSIZE);

      lpRect->OffsetRect(rcWA.right-lpRect->right,0);

      AdjustPos(lpRect);

      return TRUE;

  }

  if(iY + lpRect->Height() > rcWA.bottom - DETASTEP && iY !=rcWA.bottom-lpRect->Height())

  {//调整下

      //pWnd->SetWindowPos(NULL ,iX,rcWA.bottom-rcW.Height(),0,0,SWP_NOSIZE);

      lpRect->OffsetRect(0,rcWA.bottom-lpRect->bottom);

      return TRUE;

  }

  return FALSE;

}

//然后在ONMOVEING事件中使用所下过程调用

  CRect r=*pRect;

  AdjustPos(&r);

  *pRect=(RECT)r;



十二、给系统菜单添加一个菜单项

给系统菜单添加一个菜单项需要进行下述三个步骤:

首先,使用Resource Symbols对话(在View菜单中选择Resource Symbols...可以显示该对话)定义菜单项ID,该ID应大于0x0F而小于0xF000;

其次,调用CWnd::GetSystemMenu获取系统菜单的指针并调用CWnd:: Appendmenu将菜单项添加到菜单中。下例给系统菜单添加两个新的

int CMainFrame:: OnCreate (LPCREATESTRUCT lpCreateStruct)

{



//Make sure system menu item is in the right range.

ASSERT(IDM_MYSYSITEM<0xF000);

//Get pointer to system menu.

CMenu* pSysMenu=GetSystemMenu(FALSE);

ASSERT_VALID(pSysMenu);

//Add a separator and our menu item to system menu.

CString StrMenuItem(_T ("New menu item"));

pSysMenu->AppendMenu(MF_SEPARATOR);

pSysMenu->AppendMenu(MF_STRING, IDM_MYSYSITEM, StrMenuItem);





}



十三、运行其它程序

  //1、运行EMAIL或网址

char szMailAddress[80];  

    strcpy(szMailAddress,"mailto:netvc@21cn.com");

    ShellExecute(NULL, "open", szMailAddress, NULL, NULL, SW_SHOWNORMAL);

//2、运行可执行程序
            WinExec("notepad.exe",SW_SHOW);   //运行计事本



十四、动态增加或删除菜单

1、 增加菜单

//添加

CMenu *mainmenu;

mainmenu=AfxGetMainWnd()->GetMenu(); //得到主菜单

(mainmenu->GetSubMenu (0))->AppendMenu (MF_SEPARATOR);//添加分隔符

(mainmenu->GetSubMenu (0))->AppendMenu(MF_STRING,ID_APP_ABOUT,_T("Always on &Top")); //添加新的菜单项

DrawMenuBar(); //重画菜单



2、 删除菜单

//删除

    CMenu *mainmenu;

mainmenu=AfxGetMainWnd()->GetMenu(); //得到主菜单

CString str ;

for(int i=(mainmenu->GetSubMenu (0))->GetMenuItemCount()-1;i>=0;i--) //取得菜单的项数。

{

  (mainmenu->GetSubMenu (0))->GetMenuString(i,str,MF_BYPOSITION);

  //将指定菜单项的标签拷贝到指定的缓冲区。MF_BYPOSITION的解释见上。

  if(str=="Always on &Top") //如果是刚才我们增加的菜单项,则删除。

  {

    (mainmenu->GetSubMenu (0))->DeleteMenu(i,MF_BYPOSITION);

    break;

}



十五、改变应用程序的图标
  静态更改: 修改图标资源IDR_MAINFRAME。它有两个图标,一个是16*16的,另一个是32*32的,注意要一起修改。
  动态更改: 向主窗口发送WM_SETICON消息.代码如下:
  HICON hIcon=AfxGetApp()->LoadIcon(IDI_ICON);
  ASSERT(hIcon);
  AfxGetMainWnd()->SendMessage(WM_SETICON,TRUE,(LPARAM)hIcon);



十六、另一种改变窗口标题的方法

使用语句 CWnd* m_pCWnd = AfxGetMainWnd( ),然后,再以如下形式调用SetWindowText()函数:
SetWindowText( *m_pCWnd,(LPCTSTR)m_WindowText);// m_WindowText可以是一个CString类的变量。



十七、剪切板上通过增强元文件拷贝图像数据

下面代码拷贝通过元文件拷贝图像数据到任何应用程序,其可以放置在CView派生类的函数中。

    CMetaFileDC * m_pMetaDC = new CMetaFileDC();

    m_pMetaDC->CreateEnhanced(GetDC(),NULL,NULL,"whatever");

    //draw meta file

    //do what ever you want to do: bitmaps, lines, text...



    //close meta file dc and prepare for clipboard;

    HENHMETAFILE hMF = m_pMetaDC->CloseEnhanced();



    //copy to clipboard

    OpenClipboard();

    EmptyClipboard();

    ::SetClipboardData(CF_ENHMETAFILE,hMF);

    CloseClipboard();

    //DeleteMetaFile(hMF);

    delete m_pMetaDC;



十八、剪切板上文本数据的传送

把文本放置到剪接板上:

    CString source;

    //put your text in source

    if(OpenClipboard())

    {

        HGLOBAL clipbuffer;

        char * buffer;

        EmptyClipboard();

        clipbuffer = GlobalAlloc(GMEM_DDESHARE, source.GetLength()+1);

        buffer = (char*)GlobalLock(clipbuffer);

        strcpy(buffer, LPCSTR(source));

        GlobalUnlock(clipbuffer);

        SetClipboardData(CF_TEXT,clipbuffer);

        CloseClipboard();

    }

从剪接板上获取文本:

    char * buffer;

    if(OpenClipboard())

    {

       

        buffer = (char*)GetClipboardData(CF_TEXT);

        //do something with buffer here

        //before it goes out of scope

       

    }



    CloseClipboard();



十九、将捕捉屏幕图像到剪切版中

void CShowBmpInDlgDlg::OnCutScreen()

{

  ShowWindow(SW_HIDE);

  RECT r_bmp={0,0,::GetSystemMetrics(SM_CXSCREEN),

      ::GetSystemMetrics(SM_CYSCREEN)};



  HBITMAP hBitmap;

  hBitmap=CopyScreenToBitmap(&r_bmp);

 

  //hWnd为程序窗口句柄

  if (OpenClipboard())

  {

      EmptyClipboard();

      SetClipboardData(CF_BITMAP, hBitmap);

      CloseClipboard();

  }

  ShowWindow(SW_SHOW);

}



HBITMAP CShowBmpInDlgDlg::CopyScreenToBitmap(LPRECT lpRect)

{

  //lpRect 代表选定区域

  {

      HDC hScrDC, hMemDC;

      // 屏幕和内存设备描述表

      HBITMAP hBitmap, hOldBitmap;

      // 位图句柄

      int nX, nY, nX2, nY2;

      // 选定区域坐标

      int nWidth, nHeight;

      // 位图宽度和高度

      int xScrn, yScrn;

      // 屏幕分辨率

     

      // 确保选定区域不为空矩形

      if (IsRectEmpty(lpRect))

        return NULL;

      //为屏幕创建设备描述表

      hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL);

      //为屏幕设备描述表创建兼容的内存设备描述表

      hMemDC = CreateCompatibleDC(hScrDC);

      // 获得选定区域坐标

      nX = lpRect->left;

      nY = lpRect->top;

      nX2 = lpRect->right;

      nY2 = lpRect->bottom;

      // 获得屏幕分辨率

      xScrn = GetDeviceCaps(hScrDC, HORZRES);

      yScrn = GetDeviceCaps(hScrDC, VERTRES);

      //确保选定区域是可见的

      if (nX<0)

        nX = 0;

      if (nY<0)

        nY = 0;

      if (nX2>xScrn)

        nX2 = xScrn;

      if (nY2>yScrn)

        nY2 = yScrn;

      nWidth = nX2 - nX;

      nHeight = nY2 - nY;

      // 创建一个与屏幕设备描述表兼容的位图

      hBitmap = CreateCompatibleBitmap

        (hScrDC, nWidth, nHeight);

      // 把新位图选到内存设备描述表中

      hOldBitmap =(HBITMAP)SelectObject(hMemDC, hBitmap);

      // 把屏幕设备描述表拷贝到内存设备描述表中

      BitBlt(hMemDC, 0, 0, nWidth, nHeight,

        hScrDC, nX, nY, SRCCOPY);

      //得到屏幕位图的句柄

      hBitmap = (HBITMAP)SelectObject(hMemDC, hOldBitmap);

      //清除

      DeleteDC(hScrDC);

      DeleteDC(hMemDC);

      // 返回位图句柄

      return hBitmap;

  }

}




二十、如何将位图缩放显示在Static控件中

//在Staic控件内显示位图

void CShowBmpInDlgDlg::ShowBmpInStaic()

{

    CBitmap hbmp;

    HBITMAP hbitmap;

   

    //将pStatic指向要显示的地方

    CStatic *pStaic;

    pStaic=(CStatic*)GetDlgItem(IDC_IMAGE);



    //装载资源 MM.bmp是我的一个文件名,用你的替换

    hbitmap=(HBITMAP)::LoadImage (::AfxGetInstanceHandle(),"MM.bmp",

        IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);

   

    hbmp.Attach(hbitmap);



    //获取图片格式

    BITMAP bm;

    hbmp.GetBitmap(&bm);



    CDC dcMem;

    dcMem.CreateCompatibleDC(GetDC());

    CBitmap *poldBitmap=(CBitmap*)dcMem.SelectObject(hbmp);



    CRect lRect;

    pStaic->GetClientRect(&lRect);

    lRect.NormalizeRect();



    //显示位图

    pStaic->GetDC()->StretchBlt(lRect.left ,lRect.top ,lRect.Width(),lRect.Height(),

        &dcMem,0 ,0,bm.bmWidth,bm.bmHeight,SRCCOPY);



    dcMem.SelectObject(&poldBitmap);  

}





CNBIE BLOG
--------------------------------------------------------------------------------

VC快捷键:
原文:VC快捷键:

F1: 帮助

Ctrl+O   :Open
Ctrl+P   :Print
Ctrl+N   :New
Ctrl+Shift+F2 :清除所有书签
F2   :上一个书签
Shift+F2 :上一个书签
Alt+F2   :编辑书签
Ctrl+F2 :添加/删除一个书签
F12   :Goto definition
Shift+F12 :Goto reference
Ctrl+'Num+' :Displays the next symbol definition or reference
Ctrl+'Num-' :Displays the previous symbol definition or reference
Ctrl+J/K :寻找上一个/下一个预编译条件
Ctrl+Shift+J/K :寻找上一个/下一个预编译条件并将这一块选定
Ctrl+End :文档尾
Ctrl+Shift+End :选定从当前位置到文档尾
Ctrl+Home :文档头
Ctrl+Shift+Home :选定从当前位置到文档头
Ctrl+B/Alt+F9 :编辑断点
Alt+F3/Ctrl+F :查找
F3   :查找下一个
Shift+F3 :查找上一个
Ctrl+]/Ctrl+E :寻找下一半括弧
Ctrl+Shift+] :寻找下一半括弧并选定括弧之间的部分(包括括弧)
Ctrl+Shift+E :寻找下一半括弧并选定括弧之间的部分(包括括弧)
F4   :寻找下一个错误/警告位置
Shift+F4 :寻找上一个错误/警告位置
Shift+Home :选定从当前位置到行首
Shift+End :选定从当前位置到行尾
Ctrl+L   :剪切当前行
Ctrl+Shift+L :删除当前行
Alt+Shift+T :交换当前行和上一行
Ctrl+Alt+T :Brings up the completion list box
Shift+PageDown :选定从当前位置到下一页当前位置
Shift+PageUp :选定从当前位置到上一页当前位置
Ctrl+Shift+Space:显示函数参数的Tooltip
Ctrl+Z/Alt+Backspace :Undo
Ctrl+Shift+Z/Ctrl+Y :Redo
F8   :当前位置变成选定区域的头/尾(再移动光标或者点鼠标就会选定)
Ctrl+Shift+F8 :当前位置变成矩形选定区域的头/尾(再移动光标或者点鼠标就会选定)
Alt+F8   :自动格式重排
Ctrl+G   :Goto
Ctlr+X/Shift+Del:Cut
Ctrl+C/Ctrl+Ins :Copy
Ctrl+V/Shift+Ins:Paste
Ctrl+U   :将选定区域转换成小写
Ctrl+Shift+U :将选定区域转换成大写
Ctrl+F8   :当前行变成选定区域的头/尾(再移动上下光标或者点鼠标就会选定多行)
Ctrl+Shift+L :删除从当前位置到行尾
Ctrl+Shift+8 :将所有Tab变成`或者还原
Ctrl+T   :显示变量类型
Ctrl+↑   :向上滚屏
Ctrl+↓   :向下滚屏
Ctrl+Del :删除当前单词的后半截(以光标为分割)
Ctrl+Backspace :删除当前单词的前半截(以光标为分割)
Ctrl+←   :移到前一个单词
Ctrl+→   :移到后一个单词
Ctrl+Shift+← :选定当前位置到前一个单词
Ctrl+Shift+→ :选定当前位置到后一个单词
Ctrl+Shift+T :将本单词和上一个单词互换


Alt+0   :Workspace Window
Alt+2   :Output Window
Alt+3   :Watch Window
Alt+4   :Variables Window
Alt+5   :Registers Window
Alt+6   :Memory Window
Alt+7   :CallStack Window
Alt+8   :Disassembly Window
Ctrl+W   :ClassWizard
Alt+Enter :属性

Alt+F7   :Project Settings

F7   :Build
Ctrl+F7   :Compile
Ctrl+F5   :Run
Ctrl+Break :Stops the build
F5   :Go
Ctrl+F10 :Run to cursor
F11   :step into
Alt+F10   :Apply codes changes
Ctrl+F9   :Enable/Disable a breakpoint
Alt+F11   :将 Memory Window 切换到下一种显示模式
Alt+Shift+F11 :将 Memory Window 切换到上一种显示模式
Ctrl+Shift+F9 :去掉所有断点
Ctrl+Shift+F5 :Restarts the program
Ctrl+Shift+F10 :将当前行设为下一条指令执行的行
Alt+Num* :滚动到当前指令
Shift+F11 :跳出当前函数
F9   :断点
F10   :step over
Shift+F5 :停止 Debugging
Ctrl+F11 :Switches between the source view and the disassembly view for this instruction
Alt+F12   :Queries on the selected object or current context

Alt+F6   :Toggles the docking feature for the window on/off
Shift+Esc :隐藏窗口



Ctrl+Shift+G :?
Ctrl+*   :打开string table
Ctrl+Space :?
Ctrl+F3   :向下查找下一个
Ctrl+Shift+F3 :查找上一个
Ctrl+D   :查找
Ctrl+I   :向下查找下一个
Ctrl+Shift+I :查找上一个
F6   :?Activates the next pane
Shift+F6 :?Activates the previous pane
Ctrl+M   :?Detects duplicate mnemonics in the resource
Alt +O : 头文件与cpp文件的交互显示







CNBIE BLOG
--------------------------------------------------------------------------------

VC里面如何拆分含汉字与字母的字符串
原文:VC里面如何拆分含汉字与字母的字符串

给别人的程序打补丁,出现了需要拆分含汉字,字母的字符串的情况,到网上搜到的都是同一段代码

"************* 截取字符串 **************
Function InterceptString(txt,length)
txt=trim(txt)
x = len(txt)
y = 0
if x >= 1 then
for ii = 1 to x
if asc(mid(txt,ii,1)) < 0 or asc(mid(txt,ii,1)) >255 then "如果是汉字
y = y + 2
else
y = y + 1
end if
if y >= length then
txt = left(trim(txt),ii) "字符串限长
exit for
end if
next
InterceptString = txt
else
InterceptString = ""
end if
End Function

结果就是测试有些情况下拆分出现乱码,郁闷了好半天,终于发现是网上的这段到处转贴的代码是有错误的,其实这个错误很简单的,就是因为自己没有仔细检查一下就用结果,^_^,这次偶给更正了,希望以后看到的人不会郁闷了.

void CAaaView::OnButton1()
{
// TODO: Add your control notification handler code here
CString ChargeItemName;
CString aa = "9494858受得失测试585858585888d888888888888888";
int len=0;
ChargeItemName=InterceptString(len,aa);
AfxMessageBox(ChargeItemName);          

len=ChargeItemName.GetLength();
ChargeItemName=aa.Mid(len);
AfxMessageBox(ChargeItemName);
}
CString CAaaView::InterceptString(int qlen, CString strSource)
{
int len,i,y;
CString sTemp,sreturn,ceshi;

strSource.TrimLeft();strSource.TrimRight();
len=strSource.GetLength();
y=0;
sTemp=strSource.Right(len-qlen);

for(i=0;i<len;i++)
{
if(sTemp[y]<0 || sTemp[y]>255)
  y=y+2;
else
  y=y+1;
if(y>=26)
  break;
}
ceshi.Format("%d",y);
AfxMessageBox(ceshi);
sreturn=sTemp.Left(y);

return sreturn;
}



CNBIE BLOG
--------------------------------------------------------------------------------

VC如何实现透明窗口
原文:VC如何实现透明窗口
 
以图片为透明界面,下面的方法只适用WINDOWS2000和XP系统:


在对话框初始化函数中加入:
#define LWA_COLORKEY ?0x00000001
#define WS_EX_LAYERED ?0x00080000

typedef BOOL (WINAPI *lpfnSetLayeredWindowAttributes)(HWND hWnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);
lpfnSetLayeredWindowAttributes SetLayeredWindowAttributes;

//设置成边缘透明
COLORREF maskColor=RGB(0,255,0);
HMODULE hUser32 = GetModuleHandle("user32.dll");
SetLayeredWindowAttributes = (lpfnSetLayeredWindowAttributes)GetProcAddress(hUser32,"SetLayeredWindowAttributes");
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE) │ WS_EX_LAYERED);
? ?SetLayeredWindowAttributes(GetSafeHwnd(), maskColor, 255, LWA_COLORKEY);
FreeLibrary(hUser32);


其中maskcolor是透明颜色,也就是说把什么颜色区域设置成透明





CNBIE BLOG
--------------------------------------------------------------------------------

vc入门宝典(一)(菜单)
原文:vc入门宝典(一)(菜单)

                  菜单    

何志丹

菜单项属性说明:
ID 标识菜单的唯一常量。
Caption 菜单项标题,“&" 后面的字符为快捷键,在菜单项后的字符将加下划线。
Separator 水平线,其它属性无效。
Pop_up   有子菜单
Grayed   无效,标题以灰色显示
Inactive 无效,标题正常显示
Checked 在标题前加一个对钩
break   为None时,使它和它的兄弟们一行或一列显示。
Help   只对最上层菜单项有效,使它及后面的最上层菜单移到窗口的右上角。
Prompt 当鼠标指向它时的提示信息
多文档应用程序除了生成IDR_MAINFRAME外,还生成标识符为IDR_xxxxTYPE,其中xxxx为应用程序名。它们分别对应无文档和有文档时。
一个菜单id可以在多个类有响应函数,但只会有一个响应。
我实验得出的结果,CChildFrame,CCMenuApp,CCMenuDoc,CCMenuView,CMainFrame(我的应用程序名为CMenu)的响应顺序为:
在IDR_MAINFRAME中,CMainFrame,CCMenuApp其它三个不响应.
在IDR_XXXXTYPE 中,CCMenuView,CCMenuDoc,CChildFrame,CCMenuApp,CMainFrame.
ctrl+w打开ClassWizard,选好工程,类(最常选的是xxxView),在id中选择我们要修改的菜单项。
双击COMMAND(或UPDATE_COMMAND_UI)点确定就可以了。再在成员函数中双击我们刚刚加的函数,就可以编辑函数了。
当用户单击菜单时,我们刚刚编辑的函数会执行。
如果我们双击的是UPDATA_COMMAND_UI,则响应形式类似如下:
void ... OnUpdate...(CCmdUI * pCmdUI)
{
pCmdUI->SetCheck(true);//在菜单项前加一个对钩
pCmdUI->Enable(true);// 使菜单项能够使用
}
因为此函数往往影响到它的外形,故在它的“父亲”或“祖父”被选中时就会执行。

CWnd类中与菜单有关的几个函数。
SetMeun(CMenu *pMenu);
修改窗口的菜单,为NULL,则表示删除。
常用的还有
GetMenu();
GetSystemMenu();

CMenu的一些函数。
AppendMenu()函数指定的菜单最后附加一个新菜单项,同时可以指定菜单项的相关情况,它有两个语法。
nFlag指定状态,可以是以下四组之一或相组合而成,还可以与MF_POPUP组合表示添加的是弹出式菜单。
MF_CHECKED,MF_CHECKED
MF_DISABLED,MF_ENABLED,MF_GRAYED
MF_STRING,MF_OWNERDRAW,MF_SEPARATOR,MF_BITMAP菜单项是字符串,自画型,分隔线,位图。
MF_MENUBARBREAK,MF_MENUBREAK
nIDNewItem 指定菜单项的id.
lpszNewItem指定菜单项的内容,与nFlag有关。为MF_OWNERDRAW时,该参数为数据指针,用来传送数据,系统在发送消息WM_MEASUREITEM和WM_DRAWITEM时将该数据存入参数
的(DRAWITEMSTRUCT结构)itemData域;nFlag为MF_STRING时该参数为菜单标题。
InsertMenu
nFlags指定菜单项位置和状态,状态选项参见AppendMenu()函数,位置选项为MF_BYCOMMAND,MF_BYPOSITION.
nPositin若为MF_BYCOMMAND,新菜单项插在指定菜单项之前;或为MF_BYPOSITION,该参数指定新菜单项的位置,为-1插到最后。
ModifyMenu()参数与InsertMenu类似。
DeleteMenu删除菜单项
RemoveMenu移去菜单项

设置和显示浮动菜单
BOOL TrackPopupMenu(UINT nFlags,int x,int y,
            CWnd *pWnd,LPCRECT = NULL);
nFlag浮动式菜单坐标设定方式及鼠标操作方式,有效值如下:
TPM_CENTERALIGN   TPM_LEFTALIGN TPM_RIGHTALIGN
TPM_LEFTBUTTON   TPM_RIGHTBUTTON
x,y浮动式菜单坐标
pWnd指定操作菜单的窗口
lpRect指定鼠标操作范围
在客户区单击左键就会弹出快捷菜单,方法二需要在资源编辑器中编辑一个新菜单,方法三必须有主菜单。
方法一:
void CHeView::OnLButtonDown(UINT nFlags, CPoint point)
{
CMenu PopupMenu;
PopupMenu.CreatePopupMenu();
PopupMenu.AppendMenu(MF_STRING,ID_FILE_NEW,"NEW..");
//...

ClientToScreen(&point);
PopupMenu.TrackPopupMenu(TPM_CENTERALIGN|TPM_RIGHTBUTTON,point.x,point.y,this);


CView::OnLButtonDown(nFlags, point);
}

方法二:
void CHeView::OnLButtonDown(UINT nFlags, CPoint point)
{

CMenu menu;
menu.LoadMenu(IDR_DUMMY);
CMenu *pMenu=menu.GetSubMenu(0);
ASSERT(pMenu!=NULL);

ClientToScreen(&point);
    pMenu->TrackPopupMenu(TPM_CENTERALIGN|TPM_RIGHTBUTTON,point.x,point.y,this);
CView::OnLButtonDown(nFlags, point);
}



方法三:
void CHeView::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd *pWnd=AfxGetApp()->GetMainWnd();
CMenu * pMenu=pWnd->GetMenu();
pMenu=pMenu->GetSubMenu(0);
  ASSERT(pMenu!=NULL);

ClientToScreen(&point);
pMenu->TrackPopupMenu(TPM_CENTERALIGN|TPM_RIGHTBUTTON,point.x,point.y,this);

}
习题:
动态菜单,用户点击“更多菜单”,增加一些菜单项。
其实,自画菜单原理不难理解.AppendMenu的风格选自画,将自画用的信息(指针)强制转化成LPCTSTR,再重

载DrawItem就行了,注意自画用的信息不要提前delete了.
示例如下:
COwnerMenu.h中
class CMenuItem
{
public:
CString m_szText;
COLORREF m_color;
CMenuItem(CString szText,COLORREF color)
{
m_szText = szText;
m_color = color;
}

};

#include <afxtempl.h>

class COwnerMenu : public CMenu
{
public:
void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
bool AppendMenu(UINT nIDNewItem,CString caption,COLORREF color);
COwnerMenu();

CTypedPtrList<CPtrList,CMenuItem *> m_MenuList;
virtual ~COwnerMenu();

};

COwnerMenu.cpp中
COwnerMenu::~COwnerMenu()
{
while(m_MenuList.GetCount())
{
CMenuItem *pMenuItem = m_MenuList.GetHead();
delete pMenuItem;
m_MenuList.RemoveHead();
}
}

bool COwnerMenu::AppendMenu(UINT nIDNewItem, CString caption, COLORREF color)
{
CMenuItem * pMenuItem = new CMenuItem(caption,color);
m_MenuList.AddTail(pMenuItem);

return CMenu::AppendMenu(MF_OWNERDRAW,nIDNewItem,(LPCTSTR)pMenuItem);
}

void COwnerMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
pDC->SetTextColor(((CMenuItem*)lpDrawItemStruct->itemData)->m_color);
pDC->TextOut(0,0,((CMenuItem*)lpDrawItemStruct->itemData)->m_szText);

}

在使用菜单的地方(不要忘记了加头文件):
void COwnerMenuView::OnRButtonDown(UINT nFlags, CPoint point)
{
COwnerMenu menu;
menu.CreatePopupMenu();

menu.AppendMenu(ID_1,"1",RGB(0,0,128));
ClientToScreen(&point);

menu.TrackPopupMenu(TPM_LEFTBUTTON|TPM_LEFTALIGN,point.x,point.y,this);
CView::OnRButtonDown(nFlags, point);
}





CNBIE BLOG
--------------------------------------------------------------------------------

VC入门宝典三(String)
原文:VC入门宝典三(String)

CString:office" />

何志丹

主要内容:

1,主要函数的实现

2,常用函数

3.CString与char []的相互转换

4,将NULL字节放入CString中




vc中最主要函数不易理解。

CString::CString(char *p)

{

    int n=strlen(p);

    m_data = new char[n+1];

    strcpy(m_data,p);

}



CString::CString(CString &other)

{

    int n=strlen(other.m_data);

    m_data = new char[n+1];

    strcpy(m_data,other.m_data);

}



CString& CString::operator = (CString& other)

{

    delete[] m_data;

    int n=strlen(other.m_data);

    m_data = new char[n+1];

    strcpy(m_data,other.m_data);

    return *this;

}



String::~String()

{

    delete [] m_data;

}



Collate,Compare     与一个字符长指针所指的字符串进行比较,与strcmp相同,它们的区别是,分别调用_tcscoll,_tcsicmp。

Delete

int Delete( int nIndex, int nCount = 1 )

返回值是被删除前的字符串的长度,nIndex是第一个被删除的字符,nCount是一次删除几个字符。根据我实验得出的结果:当nCount>字符串的长度时会出错,当nCount过大,没有足够的字符删除时,此函数不执行。



FindOneOf

int FindOneOf( LPCTSTR lpszCharSet ) const;

此函数的功能是在查找lpszCharSet中的任意一个字符,查到一个就把位置返回,没有查到返回0。如:

CString str = "0123456789";

int x = str.FindOneOf("31");

x的值是1。



Find

int Find( TCHAR ch ) const;

int Find( LPCTSTR lpszSub ) const;

int Find( TCHAR ch, int nStart ) const;

int Find( LPCTSTR pstr, int nStart ) const;

返回值查找到的序号,ch待搜索的字符,lpszSub待搜索的字符子串,nStart 从那里开始搜索。如:



CString str = "0123456789";

int x = str.Find("34",4);

返回的值是-1.



GetAt

TCHAR GetAt( int nIndex ) const;

返回标号为nIndex的字符,你可以把字符串理解为一个数组,GetAt类似于[].注意nIndex的范围,如果不合适会有调试错误。



Insert

int Insert( int nIndex, TCHAR ch )
int Insert( int nIndex, LPCTSTR pstr )
返回修改后的长度,nIndex字符(或字符串)插入后的标号。

Left

CString Left( int nCount ) const;

返回的字符串的前nCount个字符。

Right与Left类似



MakeLower ,MakeUpper改变字符的大小写

MakeReverse字符倒置,如:

    CString str = "X0123456789";

    str.MakeReverse();

str变为"9876543210X"



+=

const CString& operator +=( const CString& string );
const CString& operator +=( TCHAR ch );
const CString& operator +=( LPCTSTR lpsz );

将参数合并到自己身上。

如:     CString str = "0123456789";

        str+="ha";

str为"0123456789ha";



str[]

TCHAR operator []( int nIndex ) const;

象处理字符数组一样处理字符串。

注意是只读的。

CString str = "0123456789";

    str[0]='x';

是错误的。





TrimLeft,TrimRight

void TrimLeft( );

void CString::TrimLeft( TCHAR chTarget );

void CString::TrimLeft( LPCTSTR lpszTargets );

void TrimRight( );

void CString::TrimRight( TCHAR chTarget );

void CString::TrimRight( LPCTSTR lpszTargets );



CString str = "\n\t a";

str.TrimLeft();

str为“a”;

  如果没有参数,从左删除字符(\n\t空格等),至到遇到一个非此类字符.

当然你也可以指定删除那些字符.

如果指定的参数是字符串,那么遇上其中的一个字符就删除.

CString str = "abbcadbabcadb ";

str.TrimLeft("ab");

结果"cadbabcadb "



int CString::Remove( TCHAR ch );

ch删除的字符.

返回删除字符的个数,有多个时都会删除.



数组之间的相互转换>CString 与char []之间的转换.  

char str[100] = ”str”;

CString sstr = “sstr”;

str.Format(“%s”,str);

str = LPCTSTR sstr;

strcpy(str,(LPCTSTR)sstr);

如果是赋值,则要:

CString s(_T("This is a test "));
LPTSTR p = s.GetBuffer();
// 在这里添加使用p的代码
if(p != NULL) *p = _T('\0');
s.ReleaseBuffer();
// 使用完后及时释放,以便能使用其它的CString成员函数


str的值变了.



将NULL字节放入CString中
1,CString str("abc\0""def", 7);
str +="g";
int nLength = str.GetLength();
nLength为8.
2,CString str("My name is hedan!");
str.SetAt(5, 0);
int nLength = str.GetLength();
注意:不是所有的CString成员函数都可以,在使用时一定要小心。

实例:动态配置数据源
CString strDsn,strDBQ;
strDsn = "DSN=test";
strDBQ.Format("DBQ=%s",strSourceMDBName);
CString strConnect = strDsn + " " + strDBQ ;
strConnect.SetAt(strDsn.GetLength(),'\0');
SQLConfigDataSource(NULL, ODBC_ADD_SYS_DSN, "Microsoft Access Driver (*.mdb)", strConnect);

posted on 2006-03-07 21:59 无为斋 阅读(3010) 评论(2)  编辑 收藏 引用

评论

# re: VC环境下对函数调用的汇编分析 2006-05-30 14:07 funny

想问一个VC++连接SQL SERVER 2000个人版的问题.

提示错误为:warning C4129: 'S' : unrecognized character escape sequence


连接时我的代码为:
m_pConnection.CreateInstance("ADODB.Connection");

m_pConnection->Open("Provider=SQLOLEDB;Server=JINAN-JEL9I8QQ2\STORE;Database=FStore; uid=sa; pwd=funny817","","",adModeUnknown);


可是却无法连接上,请问原因???  回复  更多评论   

# re: VC环境下对函数调用的汇编分析 2006-05-30 14:10 funny

.h的代码为:
#import "c:\Program Files\Common Files\System\ado\msado15.dll" no_namespace rename("EOF","adoEOF") rename("BOF","adoBOF")

#if !defined(AFX_ADOCONN_H__79514B46_ED08_4C99_BFB1_200CDAC82054__INCLUDED_)
#define AFX_ADOCONN_H__79514B46_ED08_4C99_BFB1_200CDAC82054__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

class ADOConn
{
// 定义变量
public:
//添加一个指向Connection对象的指针:
_ConnectionPtr m_pConnection;
//添加一个指向Recordset对象的指针:
_RecordsetPtr m_pRecordset;
// 定义方法
public:
ADOConn();
virtual ~ADOConn();
// 初始化—连接数据库
void OnInitADOConn();
// 执行查询
_RecordsetPtr& GetRecordSet(_bstr_t bstrSQL);
// 执行SQL语句,Insert Update _variant_t
BOOL ExecuteSQL(_bstr_t bstrSQL);
void ExitConnect();

};



.cpp的代码为:

#include "stdafx.h"
#include "Fstore.h"
#include "ADOConn.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

ADOConn::ADOConn()
{

}

ADOConn::~ADOConn()
{

}
void ADOConn::OnInitADOConn()
{
// 初始化OLE/COM库环境
::CoInitialize(NULL);

try
{
// 创建Connection对象
m_pConnection.CreateInstance("ADODB.Connection");

m_pConnection->Open("Provider=SQLOLEDB;Server=JINAN-JEL9I8QQ2\STORE;Database=FStore; uid=sa; pwd=funny817","","",adModeUnknown);
}
// 捕捉异常
catch(_com_error e)
{
// 显示错误信息
AfxMessageBox(e.Description());
}
}

// 执行查询
_RecordsetPtr& ADOConn::GetRecordSet(_bstr_t bstrSQL)
{
try
{
// 连接数据库,如果Connection对象为空,则重新连接数据库
if(m_pConnection==NULL)
OnInitADOConn();
// 创建记录集对象
m_pRecordset.CreateInstance(__uuidof(Recordset));
// 取得表中的记录
m_pRecordset->Open(bstrSQL,m_pConnection.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);
}
// 捕捉异常
catch(_com_error e)
{
// 显示错误信息
AfxMessageBox(e.Description());
}
// 返回记录集
return m_pRecordset;
}

// 执行SQL语句,Insert Update _variant_t
BOOL ADOConn::ExecuteSQL(_bstr_t bstrSQL)
{
// _variant_t RecordsAffected;
try
{
// 是否已经连接数据库
if(m_pConnection == NULL)
OnInitADOConn();
// Connection对象的Execute方法:(_bstr_t CommandText,
// VARIANT * RecordsAffected, long Options )
// 其中CommandText是命令字串,通常是SQL命令。
// 参数RecordsAffected是操作完成后所影响的行数,
// 参数Options表示CommandText的类型:adCmdText-文本命令;adCmdTable-表名
// adCmdProc-存储过程;adCmdUnknown-未知
m_pConnection->Execute(bstrSQL,NULL,adCmdText);
return true;
}
catch(_com_error e)
{
AfxMessageBox(e.Description());
return false;
}
}

void ADOConn::ExitConnect()
{
// 关闭记录集和连接
if (m_pRecordset != NULL)
m_pRecordset->Close();
m_pConnection->Close();
// 释放环境
::CoUninitialize();
}


就是没办法链接到数据库,请大人帮忙看看!!!!  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理