这是最近看的一片短文的title,当时就很好奇。
经常查阅MSDN的程序员都会有这个印象,微软code sample中常见的是ZeroMemory,而不是语言提供的“{0}”清零功能(不过,我一直也没有问个why)。c++语法中声明对数组或纯结构(struct),可以使用例如SPerson sTest = {0}; 来将所有成员置0.
那篇文章的解释是,Microsoft使用ZeroMemory会更clear,因为“= {0}” 这样的语法有些生僻,不是所有人都可以一下子明白。
实际上,两者还是有一些区别。
其一,ZeroMemory会将结构所有字节置0,而={0}只会将成员置0,其中padding字节不变。
其二,但一个struct有构造函数或虚函数时,ZeroMemory可以,而={0}会编译不过。显然,后者起到了一些保护作用,因为对一个有虚函数的对象使用ZeroMemory时,会将其虚函数的指针置0,这是非常危险的,因为调用虚函数时,程序显然会crash。参看如下代码:
struct SPerson
{
//SPerson(){ }
char c;
float s;
};
class CTestVirtual
{
public:
CTestVirtual()
{
}
virtual int Draw()
{
return 10;
}
int a;
};
void Test()
{
char sztmp[20];
ZeroMemory(sztmp, sizeof(sztmp));
SPerson sTest = {0};
int i = sizeof(SPerson);
//CTestVirtual otv = {0}; //Compire error
CTestVirtual tv;
ZeroMemory(&tv, sizeof(tv));
tv.Draw(); //As it is an object, don't use the virtual function pointer, so don't crash.
CTestVirtual *pTv = &tv;
pTv->Draw(); //Crash!!!
}
因此,在windows平台下,对于数组或纯结构使用ZeroMemory是安全的,对于class,则使用构造函数,不要调用ZeroMemory。如果有跨平台要求,使用={0}则可以减少一些工作。
posted @
2008-12-20 15:04 Sandy 阅读(686) |
评论 (1) |
编辑 收藏
原谅我无知,对LPTSTR不是很熟悉,尽管用了半年的WIN32,今天才想起思考LPTSTR这个变量.
例如:
LPTSTR lpStr = _T("Hello");
int len1 = wcslen(lpStr); // 值为5
int len2 = sizeof(lpStr); // 值为4
原来是这样的啊,今天我终于弄明白了!
lpStr 是一个指针,它的用法应该与指针同.
犯了一个很低级的错误.记录下来.
posted @
2008-12-19 21:10 Sandy 阅读(2173) |
评论 (0) |
编辑 收藏
在LoadString的一些小用法中, 谈到了对LoadString的一点用法,万连文指出这个方法解决不够彻底,听取了他的意见,我参考了一下vc的CString的LoadString的写法.
具体在VC98\MFC\SRC\WINSTR.CPP这个文件中,我也贴出来一部分:
#ifdef _UNICODE
#define CHAR_FUDGE 1 // one TCHAR unused is good enough
#else
#define CHAR_FUDGE 2 // two BYTES unused for case of DBC last char
#endif
BOOL CString::LoadString(UINT nID)
{
// try fixed buffer first (to avoid wasting space in the heap)
TCHAR szTemp[256];
int nLen = AfxLoadString(nID, szTemp, _countof(szTemp));
if (_countof(szTemp) - nLen > CHAR_FUDGE)
{
*this = szTemp;
return nLen > 0;
}
// try buffer size of 512, then larger size until entire string is retrieved
int nSize = 256;
do
{
nSize += 256;
nLen = AfxLoadString(nID, GetBuffer(nSize-1), nSize);
} while (nSize - nLen <= CHAR_FUDGE);
ReleaseBuffer();
return nLen > 0;
}
#ifndef _AFXDLL
int AFXAPI AfxLoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf)
{
ASSERT(AfxIsValidAddress(lpszBuf, nMaxBuf*sizeof(TCHAR)));
#ifdef _DEBUG
// LoadString without annoying warning from the Debug kernel if the
// segment containing the string is not present
if (::FindResource(AfxGetResourceHandle(),
MAKEINTRESOURCE((nID>>4)+1), RT_STRING) == NULL)
{
lpszBuf[0] = '\0';
return 0; // not found
}
#endif //_DEBUG
int nLen = ::LoadString(AfxGetResourceHandle(), nID, lpszBuf, nMaxBuf);
if (nLen == 0)
lpszBuf[0] = '\0';
return nLen;
}
#endif
这段代码写的挺精妙的.
posted @
2008-12-18 19:35 Sandy 阅读(3834) |
评论 (1) |
编辑 收藏
今天在做东西的时候,用LoadString遇到了一些问题.可能大家日后也会用到,分享一下.
LoadString 从资源载入字符串,我们一般这么用。
举个例子:
TCHAR str[20];
LoadString(hInstance, IDS_STR, str, 20);
如果我们的字符串的长度不知道,或许它会变化的话,我们怎么来获得资源ID对应的字符串呢?这就要用到
LoadString的另一种用法,我们可以这样用
LPCTSTR lpcStr = (LPCTSTR)LoadString(hInstance, IDS_STR, NULL, 0);
感觉上没有什么问题啊?
但是实际应用中又出现问题了,读出的字符串没有截断处理,它包含了下一个ID包含的字符串或者更多。
怎么办?在MSDN中,LoadString已经清楚地指出
lpBuffer is set to NULL, the return value is a pointer to the requested string. The caller should cast the return value to an LPCTSTR. This pointer points directly to the resource, so the string is read-only. The length of the string, not including any terminating null character, can be found in the word preceding the string.
同时它也给出了解决办法:
To use the lpBuffer pointer, the –n flag must be set with the resource compiler, RC.
Note String resources are not null-terminated by default. When lpBuffer is set to NULL, verify whether the string resource represented by the pointer returned by LoadString is null-terminated, and if necessary, append a terminating null character to the resource before using it in your application.
一开始我没有太明白the –n flag must be set with the resource compiler, RC.的含义,很迷惑,不知道如何解决。但是在网上寻找方法的时候,发现这么一篇文章http://lak4cyut.blogspot.com/2008/08/wm-api-loadstring.html(WM API : LoadString() 另一種使用方式),我才彻底明白过来。
我使用的是VS2005,在project->properties->Resource->Command Line中添加一个 “-n”,即可。
在运行程序,正常显示了。
大家如遇相同问题,可以试试这个方法。
posted @
2008-12-17 20:07 Sandy 阅读(6587) |
评论 (8) |
编辑 收藏
最近做的很多事情都涉及快捷方式,所以整理一下。
快捷方式的格式
数字#路径 参数
数字,我不太清楚这个是代表什么含义,也没有看到确切的说法,有人说是#后的ACSII字符的数量,
路径,有相对路径,也有绝对路径。如果路径中包含空格的话,一定要用双引号括起来,否则会产生错误, 把空格以后的内容当成参数了吧,这是我认为的。
参数,有多种吧,我还没有查资料,等查到了再补充。
举个例子:
39#"\Windows\Camera.exe"
这是手机的程序中相机的快捷方式。显然39不是#后的字符的数量。
路径有时会是一些缩写,微软自己的程序会这么写,如手机中的图片和视频,其内容为22#:MSPIMG。
:MSPIMG是什么意思呢?路径,又是指代什么呢?通过查阅资料,发现它对应注册表HKEY_LOCAL_MACHINE下\\SOFTWARE\\Microsoft\\Shell\\Rai中的:MSPIMG,其中“1”对应的值为pimg.exe。
快捷方式的创建
快捷方式的创建可以通过SHCreateShortcut这个函数来创建,其原型如下:
DWORD WINAPI SHCreateShortcut(
LPTSTR szShortcut,
LPTSTR szTarget
);
例如:
SHCreateShortcut( _T("\\My Documents\\Windows Media Player.lnk"), _T("\\Windows\\Ceplayer.exe"));
快捷方式路径的获取
快捷方式的目标路径获取,可以通过SHGetShortcutTarget来获取。其原型如下:
BOOL SHGetShortcutTarget(
LPTSTR szShortcut,
LPTSTR szTarget,
int cbMax
);
例如:
TCHAR str[MAX_PATH];
SHGetShortcutTarget(_T("\\My Documents\\Windows Media Player.lnk"), str, MAX_PATH);
posted @
2008-12-14 23:10 Sandy 阅读(561) |
评论 (0) |
编辑 收藏
最近在看代码,写代码的人很喜欢用回调函数和函数指针。一直觉得回调函数和函数指针挺神秘的,所以查了一些资料,来与大家一起来分享。
什么是回调函数
简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
为什么要使用回调函数
因为使用回调函数可以把调用者和被调用者分开,调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。回调函数就好像是一个中断处理函数,系统在符合你设定的条件时自动调用。
如何使用回调函数
使用回调函数,我们需要做三件事:
- 声明
- 定义
- 设置触发条件:在你的函数种把你的回调函数名称转化为地址作为一个参数,以便于系统调用。
声明和定义时应注意,回调函数由系统调用,所以可以认为它属于windows系统,不要把它当作你的某个类的成员函数。
回调函数是一个程序员不能显示调用的函数,通过将回调函数的地址传给调用者从而实现调用。回调函数是十分必要的,在我们想通过一个统一接口实现不同的内容,这时回调函数非常合适。
函数指针的声明
对回调函数有了一个初步的了解,下面我们来说一下函数指针。因为要实现回调,必须首先定义函数指针。
void (*) ()
左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和右边圆括弧中的入口参数
为函数指针声明类型定义:
Typedef void(* pfv)()
pfv 是一个函数指针,它指向的函数没有输入参数,返回类型为voie。使用这个类型定义名称可以隐藏负责的函数指针语法。
void (*p)();
void func()
{
……
}
p = func;
p的赋值可以不同,但一定要是函数的指针,并且参数和返回类型相同。
例如:
现学现卖的一个小例子
#include <iostream>
using namespace std;
typedef void (*PF)();
void func()
{
cout << "func" << endl;
}
void caller( PF pf)
{
pf();
}
int main()
{
PF p = func;
caller(p);
system("pause");
return 0;
}
调用约定
在visual c++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示调用规范(默认为_cdecl)。调用规范影响编译器产生的给定函数名,参数传递的顺序,堆栈清理责任以及参数传递机制。
不过,在win32的程序中,我见得比较多的是CALLBACK,这个宏定义在windef.h中,
#define CALLBACK __stdcall
它约定了函数在它们返回到调用者之前,都会从堆栈中移除掉参数。
摘自:
回调函数
http://hi.baidu.com/spidermanzy/blog/item/b25b00956469c6097bf48016.html
回调函数以及钩子函数的概念
http://zq2007.blog.hexun.com/9068988_d.html
声明函数指针并实现回调
http://www.vckbase.com/document/viewdoc/?id=195
posted @
2008-12-07 16:56 Sandy 阅读(13191) |
评论 (7) |
编辑 收藏
昨天,12月5日,即农历十一月初八,我见证了别人订婚的温馨和浪漫,心里也暖暖的有一股温馨的冲动。
先简单介绍一下这两位吧,一位是即将成为成功男士的man,已经拿到了一家非常知名企业的offer,一位是小巧可爱的心理老师,两位也将在今年年末,步入婚姻的殿堂。令人流口水的羡慕。
一百朵玫瑰花,伴着香水百合,代表白头偕老,男生的“你愿意嫁给我么?”,女生激动的泪水洋溢着幸福。我们当时总共10人,除去这对,其他的是四对情侣,心里都有这么一点温馨的冲动。
幸福的时候是这么幸福,回忆过去的点滴,你会觉得男生也会这么细心,女生竟是这么体贴,幸福……
我们这一对,也走了很长的路,算算有五年多了,还在爱情长跑的路上,我们对爱很坚定,看着这么幸福的一对,我们也有所冲动。但是一切还没有完全定下来,目前还在上学。
只能将心中的冲动埋藏在心,待某一时刻,期待我们也能带给别人温馨的冲动吧。
祝福这幸福的一对,百年好合,甜甜美美,也希望我们手牵着手,走到幸福的终点。
幸福……真好!
posted @
2008-12-06 10:11 Sandy 阅读(170) |
评论 (0) |
编辑 收藏
SHGetSpecialFolderPath
作用:
获取特定文件夹路径
原型:
BOOL SHGetSpecialFolderPath(
HWND hwndOwner,
LPTSTR lpszPath,
int nFolder,
BOOL fCreate
);
示例:
获得自启动文件夹的路径
TCHAR filePath[MAX_PATH];
::SHGetSpecialFolderPath(NULL, filePath, CSIDL_STARTUP, FALSE);
以下是nFolder值的对应情况
获取值的机器为多普达838
CSIDL_STARTMENU —— \Windows\“开始”菜单
CSIDL_STARTUP —— \Windows\StartUp
CSIDL_WINDOWS —— \Windows
CSIDL_RECENT ——
CSIDL_PROGRAMS —— \Windows\“开始”菜单\程序
CSIDL_PROGRAM_FILES —— \Program Files
CSIDL_PERSONAL —— \My Documents
CSIDL_MYVIDEO ——
CSIDL_MYPICTURES —— \My Documents\我的图片
CSIDL_MYMUSIC —— \My Documents\我的音乐
CSIDL_FONTS —— \Windows\Fonts
CSIDL_FAVORITES —— \Windows\Favorites
CSIDL_DESKTOPDIRECTORY ——
CSIDL_DESKTOP —— \My Documents
CSIDL_APPDATA —— \Application Data
posted @
2008-12-05 16:35 Sandy 阅读(3846) |
评论 (0) |
编辑 收藏
摘要: 今天在网上看到的.最近在弄浏览器,对其方法要熟悉一些.贴出来,以备查询. IWebBrowser2 Interface http://msdn.microsoft.com/en-us/library/aa752127(VS.85).aspx
AddressBar
...
阅读全文
posted @
2008-12-03 13:34 Sandy 阅读(2735) |
评论 (0) |
编辑 收藏
向大家SHOW一下,我自己画的圆饼图。
呵呵,自我感觉不错。
原理很简单,是通过画多边形,并填充不同的颜色来实现的。
实际上,这个图是通过以下几个图拼成的。
这下大家清楚了很多了吧。
这个图的关键在于弧上的各点的坐标如何得到?这个圆饼的最上面的那个图形其实是一个椭圆。我们可以利用一个椭圆上点的计算公式来求的弧上点的坐标。
长轴为a,短轴为b,, 轴心为(x0, y0)那么椭圆上的某点坐标(x, y)为
x = x0 + a * cos(θ);
y= y0 + b * sin(θ) ;
通过这种方法计算弧上各点后,将弧平移,如下图:
这样我们就可以计算出柱面下半部分的弧线坐标了。
呵呵,这样就简单多了吧。
posted @
2008-12-02 22:51 Sandy 阅读(2910) |
评论 (5) |
编辑 收藏