用VS2010查看类的内存布局,这里用两种方法

(1)MSVC有个隐藏的“/d1”开关,通过这个开关可以查看项目中类的内存布局情况。

修改项目属性,添加”/d1 reportSingleClassLayoutCC”开关,表示需要查看类CC的内存布局。

/d1 reportAllClassLayout 是查看所有类的布局,这样编译后会在输出界面上输出很多类的布局

/d1 reportSingleClassLayoutXX,"XX"是填入你想查看的类的名字,编译后会在输出界面上输出类的布局,任何匹配它的类名都会被输出。比如想看class A,你输入了/d1reportSingleClassLayoutA,但是你会发现会编译器输出很多无关的类,如struct ABC、Class inAttribute Class dbA...因为它们的类名都包含有A这个字母。

(2)通过添加自定义的外部工具,将类的内存布局在cmd界面上打印出来

1、首先新建一个bat文件,可以命名为classlayout.bat,保存在VC++的目录下,就是调用了下cl.exe而已

@echo off

call "%VS120COMNTOOLS%vsvars32.bat"

if "%2" == "" goto error 
if "%1" == "" goto error
cl.exe /nologo /w /Zs /d1reportSingleClassLayout%2 %1
goto :eof
 
:error
echo Error:参数错误 - 是否划定了类名?
goto :eof

2、然后新建一个cpp文件,会创建通道并创建子进程执行批处理。然后捕获批处理的标准输出,并过滤内容,只输出想要的。。。

#include <iostream>
#include <fstream>
#include <string>
#include <windows.h>
using namespace std;

char g_name[101] = {};

bool CMDLine(char* cmd)
{
    SECURITY_ATTRIBUTES sa;
    HANDLE hRead,hWrite;

    sa.nLength=sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
    if (!CreatePipe(&hRead, &hWrite, &sa, 0))
    {
        cout << "CreatePipe失败" << endl;
        return false;
    }
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    ZeroMemory(&pi, sizeof(pi));
    si.cb = sizeof(STARTUPINFO);
    GetStartupInfo(&si);
    si.hStdError = hWrite;
    si.hStdOutput = hWrite;
    si.wShowWindow = SW_HIDE;
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    if (!CreateProcess(NULL,cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
    {
        cout << "CreateProcess失败" << endl;
        return false;
    }
    CloseHandle(hWrite);
 
    char buffer[128] = { 0 }; 
    DWORD bytesRead;
 
    bool bFindIt = false;
    bool bFindEnd = false;
    char *pBegin = NULL;
    char *pError = NULL;
////    ofstream ofile("c:\\out2.txt", ios_base::binary);    

    cout << "Waiting..." << endl;
    //WaitForSingleObject(hRead,INFINITE);
    while (ReadFile(hRead, (LPVOID)&buffer, 127, &bytesRead, NULL)) 
    {    
        buffer[bytesRead] = '\0';
//////        ofile << "Block:\n";
////        ofile << (char*)buffer;
////        continue;
        
        if(pError = strstr(buffer, "error C")) //输出错误
        {
            char *pReturn = strstr(pError, "\r");
            if(pReturn)
            {
                *pReturn = '\n';
                *(pReturn+1) = '\0';
            }
            cout << pError << (pReturn?"":"\n");
            continue;
        }

        if(!bFindIt && (pBegin = strstr(buffer, g_name)))
        {
            system("cls");
            bFindIt = true;

            char *pEnd = strstr(pBegin, "\r\n\r\n\r\n"); //每个class间至少3个
            if(pEnd)
            {
                *(pEnd+6) = '\0';
                bFindEnd = true;    
            }

            cout << "class" << pBegin;
            continue;
        }

        if(bFindIt && !bFindEnd)
        {
            char *pEnd = strstr(buffer, "\r\n\r\n\r\n");
            if(pEnd)
            {
                *(pEnd+6) = '\0';
                bFindEnd = true;
            }
            
            cout << (char*)buffer;            
        }
    }
    
////    ofile.close();
    if(false == bFindIt)
        cout << "\n未找到class" << g_name << endl << endl;

    return true;
}

int main(int argc, char*argv[])
{
    if(argc != 3)
        goto end;

    cout << argv[1] << endl;
    cout << argv[2] << endl;

    char *cppPath = argv[1];
    while(*cppPath)
        cppPath++;
    if(cppPath - argv[1] < 5 || _strnicmp(cppPath-4, ".cpp", 4))
        goto end;
    if(*argv[2] == '\0')
        goto end;
    else
        _snprintf(g_name, 100, " %s\t", argv[2]);

    char cmd[201] = {};
    _snprintf(cmd, 200, "D:\\VS2010\\VC\\bin\\classlayout.bat \"%s\" \"%s\"", argv[1], argv[2]);

    cout<<cmd<<endl;
    CMDLine(cmd);
    system("pause");
    return 0;

    end:
        cout << "error: 输入参数不正确,是否已选中类名?" << endl;
        system("pause");
        return 0;
}

这里要添加两个system("pause");否则到时候会存在闪退的情况

  1. 里面的ofile用来将所有捕获到的内容输出到文件的,想打开它,取消里面的////注释即可!!!
  2. 里面的_snprintf(g_name, 100, " %s\t", argv[2]),是为了尽可能查找到类名,若改为"class %s\t",有时会找不到,因为ReadFile是按块读取的,两个块有可能从"class %s"中间断开。。。我们这样减小了这种可能,,当然,只是减小而已。
  3. 这也会过滤error warning等
  4. struct的内存布局输出前缀也是"class"
  5. 这里提醒,如果直接复制代码,需要注意代码中的那个路径要改成你电脑上的classlayout.bat文件的路径

3、编译通过后,配置成外部工具,工具->外部工具->添加,命令:找到你的可执行文件,参数如图所示,标题任意。

4、在使用前,确保你要查看的类所在的cpp文件编译通过,使用时,先选中你要查看的类的名字,再在工具中找到你起的名字(classlayout)就可以

注意:

(1)如果你的显示CreateProcess失败,你可以看下你的项目属性中字符集选用的是不是多字节字符集

(2)如果cmd格式不对,应该也是上述设置的问题

posted on 2018-07-12 19:56  Mini_Coconut  阅读(1574)  评论(0编辑  收藏  举报