MyMSDN

MyMSDN记录开发新知道

2011年3月19日 #

关于protobuf中的field_number范围的解释

以下是来自Google Protobuf Documents里的一句话:
As you can see, each field in the message definition has a unique numbered tag. These tags are used to identify your fields in the message binary format, and should not be changed once your message type is in use. Note that tags with values in the range 1 through 15 take one byte to encode. Tags in the range 16 through 2047 take two bytes. So you should reserve the tags 1 through 15 for very frequently occurring message elements. Remember to leave some room for frequently occurring elements that might be added in the future.
这里要做一个解释,就是为什么是1到15,以及16到2047呢?
  1. 1到15,仅使用1bytes。每个byte包含两个部分,一个是field_number一个是tag,其中field-number就是protobuf中每个值后等号后的数字(在C++和Java中,如果不设置这个值,则它是随机的,如果在Python中,不设置,它则不被处理(这个在message binary format中的Field Order一节中有提到)。那么我们可以认为这个field_number是必须的。那么一个byte用来表达这个值就是000000000,其中红色表示是否有后续字节,如果为0表示没有也就是这是一个字节,蓝色部分表示field-number,绿色部分则是wire_type部分,表示数据类型。也就是(field_number << 3) | wire_type。其中wire_type只有3位,表示数据类型。那么能够表示field_number的就是5位蓝色的数字,5位数字能够表达的最大范围就是1-15(其中0是无效的)。
  2. 16到2047,与上面的规则其实类似,下面以2bytes为例子,那么就有10000000 00000000,其中红色部分依然是符号位,因为每个byte的第一位都用来表示下一byte是否和自己有关,那么对于>1byte的数据,第一位一定是1,因为这里假设是2byte,那么第二个byte的第一位也是红色,刨除这两位,再扣掉3个wire_type位,剩下11位(2*8-2-3),能够表达的数字范围就是2047(211)。
参考资料:
  1. http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/proto.html
  2. http://code.google.com/apis/protocolbuffers/docs/encoding.html

posted @ 2011-03-19 02:01 volnet 阅读(3094) | 评论 (0)编辑 收藏

zig-zag编码方式

zig-zag:就是把(-1)1000 0001变成0000 0011,注意最后一个1是符号位,也就是2,那么如果是个int32的话,1000 0000 0000 0000 0000 0000 0000 0001 就变成了000 0000 0000 0000 0000 0000 0000 00011,那么也就是11了,那么可以节约很多符号位。http://goo.gl/2wRKb
用位运算来表示把一个负数转换成zig-zag编码,就是

int32是:(
n << 1) ^ (n >> 31)
int64是:(n << 1) ^ (n >> 63)
也就是说,如果是负数,对于32位最多能省去30格(其中1格是符号位,另一个代表最小值1,此处假设“正负0”不合法)。同理,64位最多能省去62位。当然比较极端的是所有的位数都被用上了。

posted @ 2011-03-19 00:36 volnet 阅读(2096) | 评论 (0)编辑 收藏

2010年9月6日 #

IsWow64并不能用来检测是否是Windows 32bit系统还是64bit系统

如何当前操作系统是不是64位?如何判断当前应用程序是否在Wow64下运行?

首先什么是Wow64?很多朋友一看到64就认为这个方法是判断当前系统是否是64bit的,其实不然。Wow64是Windows-On-Windows64的意思,它是指在64位的操作系统上(不是指64位的CPU)运行32位应用程序的兼容平台。

下面是MSDN中一段IsWow64的应用程序:

BOOL IsWow64() 
{ 
    typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); 
    LPFN_ISWOW64PROCESS fnIsWow64Process; 
    BOOL bIsWow64 = FALSE; 
    fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( GetModuleHandle(_T("kernel32")), "IsWow64Process"); 
    if (NULL != fnIsWow64Process) 
    {
        fnIsWow64Process(GetCurrentProcess(),&bIsWow64); 
    } 
    return bIsWow64; 
}

下面的代码用来检测这个程序的结果:

if( IsWow64() == TRUE )
{
    _tprintf(_T("IsWow64() == TRUE\n"));
}
else
{
    _tprintf(_T("IsWow64() == FALSE\n"));
}

让我们编译一下这个程序。

我们需要的是64位的操作系统,比如XP64bit,Windows 2008 R2等都是64bit操作系统。

在64位的操作系统上运行的kernel32.dll中,将会实现IsWow64Process方法,而在32位系统中提供的kernel32.dll中则没有提供相关函数的实现。

比较迷惑人的则是bIsWow64,其实仔细看MSDN中的Remark,会发现:

If the application is a 64-bit application running under 64-bit Windows, the Wow64Process parameter is set to FALSE.也就是说64位应用程序跑在64位的操作系统上,bIsWow64的值将是FALSE而不是TRUE。

因此我们需要分别将我们的程序编译成Win32平台和x64平台的,如果你用Visual Studio进行编译,默认安装则只包含32位的编译器/链接器,即便你是在64位操作系统上安装,也是一样的。你需要在VC++节点下勾选x64选项才可以,Itanium则需要在Server平台下安装才可勾选。然后在编译的时候,分别选择Win32和x64进行编译。

image

编译后,运行,结果如我们分析的一样:

在64位系统上运行Win32编译配置的结果是IsWow64() == TRUE,而x64编译配置的结果是IsWow64() == FALSE。

如果想要知道当前系统是否是64位的,则可以通过下面的方法:

BOOL Is64bitSystem()
{
    SYSTEM_INFO si;
    GetNativeSystemInfo(&si);

    if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ||    
        si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 )
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    } 
}

注意:需要注意是GetNativeSystemInfo  函数从Windows XP 开始才有, 而 IsWow64Process  函数从 Windows XP with SP2 以及 Windows Server 2003 with SP1 开始才有。

posted @ 2010-09-06 02:06 volnet 阅读(5431) | 评论 (12)编辑 收藏

2010年9月5日 #

英特尔® 多线程应用开发指南

     摘要: http://software.intel.com/zh-cn/articles/intel-guide-for-developing-multithreaded-applications/ 英特尔® 多线程应用开发指南 提交新文章 Published On :  2010年02月25日 20:00 评级 请登录后评级!当前分数: 0 由 0 用户 请登录后评级!当前分数: 0 ...  阅读全文

posted @ 2010-09-05 02:20 volnet 阅读(2723) | 评论 (0)编辑 收藏

2010年9月4日 #

关于宏MAX_PATH与文件名长度的关系

目的:测试MAX_PATH路径与文件名的关系。

结论:MAX_PATH代表从盘符开始到文件名结尾的C字符串长度(长度+1)的最大长度。也就是假设C:\a.txt共8个字符,长度为9,MAX_PATH通常为260,其中这个文件全名的长度不能大于260。

测试代码:

image

#include "stdafx.h"
#include <atlbase.h>
#include <windows.h>
#include <atlfile.h>
#include <atlstr.h>
#include <iostream>
#include <string>

BOOL IsFileExist( LPCTSTR lpszFileName )
{
    DWORD dwAttr = ::GetFileAttributes( lpszFileName );
    if ( dwAttr == 0xFFFFFFFF )
    {
        return FALSE;
    }
    if ( ( dwAttr & FILE_ATTRIBUTE_DIRECTORY ) > 0 )
    {
        return FALSE;
    }
    return TRUE;
}

BOOL DeleteFiles( LPCTSTR lpszPath )
{
    TCHAR szFrom[_MAX_PATH+1] = {_T( '\0' )};
    lstrcpy( szFrom , lpszPath );
    SHFILEOPSTRUCT shf;
    memset( &shf, 0, sizeof( SHFILEOPSTRUCT ) );
    shf.hwnd = NULL;
    shf.pFrom = szFrom;
    shf.wFunc = FO_DELETE;
    shf.fFlags = FOF_NOCONFIRMMKDIR | FOF_NOCONFIRMATION |
        FOF_NOERRORUI | FOF_SILENT;
    return SHFileOperation( &shf ) == 0;

}

BOOL WriteBinaryBytesToFile( LPCTSTR fileName, unsigned char* data, 
                       unsigned int datasize , BOOL bAppend )
{
    CAtlFile file;
    if( !bAppend && IsFileExist( fileName ))
        DeleteFiles( fileName );

    HRESULT ret = file.Create( fileName, FILE_WRITE_DATA, FILE_SHARE_WRITE, bAppend?OPEN_ALWAYS:CREATE_ALWAYS );
    if ( !SUCCEEDED( ret ) )
        return FALSE;
    if( bAppend )
    {
        file.Seek( 0, FILE_END );
    }
    unsigned char *pos = data;
    while( datasize > 0 )
    {
        DWORD dwWrite = 0;
        if ( file.Write( pos , datasize , &dwWrite ) != S_OK )
        {
            file.Close();
            return FALSE;
        }
        datasize -= dwWrite;
        pos += dwWrite;
    }
    file.Close();
    return TRUE;
}

BOOL GCreateFile(std::string& fileNamePath,
                std::string& fileNameExt,
                int shortNameLength,
                std::string& data)
{
    char c[] = "a";
    std::string fileName;
    fileName.append(fileNamePath);
    while( shortNameLength-- )
    {
        fileName.append(c);
        ++*c;
        if( *c > 'z' )
        {
            *c = 'a';
        }
    }
    fileName.append(fileNameExt);

    std::cout << "fileName:" << fileName << std::endl;
    std::cout << "fileName.size():"<< fileName.size() << std::endl;
    std::cout << "MAX_PATH" << MAX_PATH << std::endl;

    if( WriteBinaryBytesToFile(fileName.c_str(), (unsigned char*)data.data(), data.size(), FALSE ) )
    {
        std::cout << "++++++++++++++WriteBinaryBytesToFile successful.++++++++++++++" << std::endl;
        return TRUE;
    }
    else
    {
        std::cout << "==============WriteBinaryBytesToFile error==============." << std::endl;
        return FALSE;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::string fileNamePath = "E:\\MyCPlusProject\\TestFileNameMaxLength\\TestFileNameMaxLength\\Output\\";
    std::string fileNameExt = ".txt";
    std::string data("This is a content.");
    
    int shortNameLength;
    shortNameLength = MAX_PATH - fileNameExt.size() - fileNamePath.size();
    GCreateFile(fileNamePath, fileNameExt, shortNameLength, data);

    std::cout << "------------------" << std::endl;
    shortNameLength = MAX_PATH - fileNameExt.size() - fileNamePath.size() - 1;
    GCreateFile(fileNamePath, fileNameExt, shortNameLength, data);

    return 0;
}

提示:右键项目属性,将字符集设置为“未设置”。

posted @ 2010-09-04 13:59 volnet 阅读(3218) | 评论 (0)编辑 收藏

进程间窗体置顶

最近遇到一个跟Windows(不是指操作系统,而是一个::CreateWindow的结果)有关的问题。

情况是这样的,通常的应用程序内的事件传递一般场景都是类似在窗体A上点击按钮1,弹出一个窗体B。面对这样的场景只要::SetWindowActive(HWND wnd)即可,这里其实有个隐含的前提,就是wnd的父窗口是出于TOP的状态,也就是它的父窗口是置顶的,也就是我们能够在界面上看到它,否则你的其他置顶窗口将依旧置顶。

下面的代码示意了如何将窗口置顶,因为也没有对各个方法做深入分析,所以就不多发表言论了:

/*场景:
    目标:将子窗口放到置顶(且是HWND_TOP)模式
    说明:hMainWnd是主窗口,m_pMMessageWnd->m_hWnd是子窗口
*/

/*1、将窗口从后台直接提到最前方,需要先将主窗口移上来。*/
HWND hMainWnd = CFramework::GetInstance()->GetMainWnd();
if( hMainWnd != NULL )
{
    SetForegroundWindow(hMainWnd);
    BringWindowToTop(hMainWnd);
    SetActiveWindow(hMainWnd);
    SetWindowPos( hMainWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW );
}

/*2、将子窗口置顶*/
if( m_pMMessageWnd != NULL ) /* true */
{
    SetForegroundWindow(m_pMMessageWnd->m_hWnd);
    BringWindowToTop(m_pMMessageWnd->m_hWnd);
    SetActiveWindow(m_pMMessageWnd->m_hWnd);
    SetWindowPos( m_pMMessageWnd->m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW );
    SetWindowPos( m_pMMessageWnd->m_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW );
}

/*3、放在后面的原因是避免子窗口还未弹出,点击窗体外的时候被放到后面去,所以等窗口出来后再取消置顶*/
if( hMainWnd != NULL )
{
    SetWindowPos( hMainWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW );
}

/*4、避免被主窗口给压了*/
if( m_pMMessageWnd != NULL ) /* true */
{
    SetWindowPos( m_pMMessageWnd->m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW );
    SetWindowPos( m_pMMessageWnd->m_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW );
}

posted @ 2010-09-04 12:41 volnet 阅读(1459) | 评论 (0)编辑 收藏

2010年8月28日 #

[转]C/C++中动态链接库的创建和调用

转载自:http://developer.51cto.com/art/200702/39608.htm

动态连接库的创建步骤:

一、创建Non-MFC DLL动态链接库

1、打开File —> New —> Project选项,选择Win32 Dynamic-Link Library —>sample project —>工程名:DllDemo

2、新建一个.h文件DllDemo.h

#ifdef DllDemo_EXPORTS
#define DllAPI __declspec(dllexport)
#else
#define DllAPI __declspec(dllimport)
extern "C" //原样编译
{
DllAPI int __stdcall Max(int a,int b); //__stdcall使非C/C++语言内能够调用API
}
#endif

3、在DllDemo.cpp文件中导入DllDemo.h文件,并实现Max(int,int)函数

#include "DllDemo.h"
DllAPI int __stdcall Max(int a,int b)
{
if(a==b)
return NULL;
else if(a>b)
return a;
else
return b;
}

4、编译程序生成动态连接库

二、用.def文件创建动态连接库DllDemo.dll

1、删除DllDemo工程中的DllDemo.h文件。

2、在DllDemo.cpp文件头,删除 #include DllDemo.h语句。

3、向该工程中加入一个文本文件,命名为DllDemo.def并写入如下语句:

LIBRARY MyDll

EXPORTS

Max@1

4、编译程序生成动态连接库。

动态链接的调用步骤:

一、隐式调用

1、建立DllCnslTest工程

2、将文件DllDemo.dll、DllDemo.lib拷贝到DllCnslTest工程所在的目录

3、在DllCnslTest.h中添加如下语句:

#define DllAPI __declspec(dllimport)
#pragma comment(lib,"DllDemo.lib") //在编辑器link时,链接到DllDemo.lib文件
extern "C"
{
DllAPI int __stdcall Max(int a,int b);
}

4、在DllCnslTest.cpp文件中添加如下语句:

#include "DllCnslTest.h"//或者 #include "DllDemo.h"
void main()
{
int value;
value = Max(2,9);
printf("The Max value is %d\n",value);
}

5、编译并生成应用程序DllCnslTest.exe

二、显式调用

1、建立DllWinTest工程。

2、将文件DllDemo.dll拷贝到DllWinTest工程所在的目录或Windows系统目录下。

3、用vc/bin下的Dumpbin.exe的小程序,查看DLL文件(DllDemo.dll)中的函数结构。

4、使用类型定义关键字typedef,定义指向和DLL中相同的函数原型指针。

例:

typedef int(*lpMax)(int a,int b); //此语句可以放在.h文件中

5、通过LoadLibray()将DLL加载到当前的应用程序中并返回当前DLL文件的句柄。

例:

HINSTANCE hDll; //声明一个Dll实例文件句柄
hDll = LoadLibrary("DllDemo.dll");//导入DllDemo.dll动态连接库

6、通过GetProcAddress()函数获取导入到应用程序中的函数指针。

例:

lpMax Max;
Max = (lpMax)GetProcAddress(hDLL,"Max");
int value;
value = Max(2,9);
printf("The Max value is %d",value);

7、函数调用完毕后,使用FreeLibrary()卸载DLL文件。

FreeLibrary(hDll);

8、编译并生成应用程序DllWinTest.exe

注:显式链接应用程序编译时不需要使用相应的Lib文件。

下载(Visual Studio 2008验证通过):http://www.cppblog.com/Files/mymsdn/DllCnsTest.7z

posted @ 2010-08-28 00:30 volnet 阅读(1067) | 评论 (1)编辑 收藏

2010年8月11日 #

两个有用的宏:“禁止类成员复制”以及“禁止隐式构造”

禁止类成员复制,其核心就在于不允许类外部看见复制函数,包括“拷贝构造函数”、“operator =重载”。

#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
private:                     \
TypeName(const TypeName&);               \
TypeName& operator=(const TypeName&)
禁止隐式构造,则可以将默认构造函数隐藏起来,在大多数编译器中也可以对构造函数增加explicit关键字来避免隐式构造。
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
private:                     \
TypeName();                                    \
DISALLOW_COPY_AND_ASSIGN(TypeName)
更多解释详见《More Effective C++》

posted @ 2010-08-11 00:34 volnet 阅读(954) | 评论 (1)编辑 收藏

2010年7月22日 #

[C++]__declspec关键字

__declspec关键字

// keyword__declspec.cpp : 定义控制台应用程序的入口点。
//

// ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/kernel_d/hh/Kernel_d/64bitAMD_6db3322a-fe6d-4287-9eda-a9c1378e715d.xml.htm
// The sizeof value for any structure is the offset of the final member, 
//  plus that member's size, rounded up to the nearest multiple of the largest 
//  member alignment value or the whole structure alignment value, 
//  whichever is greater. 

#include "stdafx.h"

__declspec( align( 32) ) struct Struct__declspec_1
{
    int a;
    int b;
};

__declspec( align( 32) ) struct Struct__declspec_2
{
    __declspec( align( 64) ) int a;
    int b;
};

__declspec( align( 8 ) ) struct Struct__declspec_3
{
    int a;  //4 bytes
    int b;  //4 bytes
    int c;  //4 bytes
};

__declspec( align( 8 ) ) struct Struct__declspec_4
{
    int a;  //4 bytes
    int b;  //4 bytes
};

struct StructNormal
{
    int a;  //4 bytes
    int b;  //4 bytes
    int c;  //4 bytes
};

int _tmain(int argc, _TCHAR* argv[])
{
    printf( "sizeof Struct__declspec_1 is %d.\n", sizeof( Struct__declspec_1 ));    //32
    printf( "sizeof Struct__declspec_2 is %d.\n", sizeof( Struct__declspec_2 ));    //64
    printf( "sizeof Struct__declspec_3 is %d.\n", sizeof( Struct__declspec_3 ));    //16
    printf( "sizeof Struct__declspec_4 is %d.\n", sizeof( Struct__declspec_4 ));    //8

    printf( "sizeof StructNormal is %d.\n", sizeof( StructNormal ));    //12
    return 0;
}

posted @ 2010-07-22 20:46 volnet 阅读(909) | 评论 (1)编辑 收藏

2010年7月6日 #

[C++]内存管理(1)

     摘要: 和大多数内存管理的初衷一致,希望能够控制内存分配和回收,减少内存碎片,且通常这样的内存都会预开一段连续内存空间,然后我们自己来管理这段内存。当然通常这样的需求都很合理,但是实现起来则通常不能完美,比如:效率、算法的选择、如何减少内存碎片、跟踪管理内存分配、性能检测、对系统内存使用的统计、垃圾回收等。下面是我近期实现的一个非常简陋的程序,甚至可能连基本的要求都无法达到,大家帮忙看看,它究竟有多少缺点...  阅读全文

posted @ 2010-07-06 22:45 volnet 阅读(3438) | 评论 (17)编辑 收藏

仅列出标题  下一页
特殊功能