Windows资源

Windows资源是一种二进制数据,由链接器链接进程序成为程序的一部分,通过资源的方式可以很方便的对应用程序进行扩展。在Windows中资源可以是系统自定义的,也可以是用户自定义的。在VC++中资源是以被称为资源脚本的文本文件描述的(扩展名为rc),另外为了方便代码中调用资源,VC++环境中还会自动生成一个resource.h的头文件供C++代码使用,这个文件中主要定义了各个资源的ID,在vc++中使用ID来唯一标识一个资源,这个ID可以是数字也可以是字符串,其实在VC中真正用来标识资源的是字符串,通过宏MAKEINTRESOURCE可以将数字型的ID转化为对应的字符串,一般的资源函数在操作资源时都需要提供一个资源的字符串,而这个串就是利用这个宏传入ID生成的。 在VC中资源脚本的基本格式为: 资源名(ID串) 类型名 [语言] 资源数据 资源数据可以是一段指定格式的文本或者一个文件,比如我们将wav作为资源加入到程序中,可以这样写: MY_WAVE_RES IDR_WAVE sample.wav.其中语言如果没有指定,那么默认为操作系统当前的语言环境。 另外我们也可以将不同的资源放入不同的文本文件中,先定义好,然后在.rc文件中使用#include 来包含进来,比如在一个名为wav.resinclude文件中定义了一个WAV资源,然后可以在.rc文件中加上一句 “#include

引用自定义资源

对于系统自定义资源,系统都提供了专门的函数来进行加载和操作,但是对于自定义资源,在操作时相对比较复杂,一般先使用FindResource和FindResourceEx在进程中找到对应的资源句柄,然后使用LoadResource将资源加载到内存中,以后就可以使用这个资源了。 下面的一个例子演示了如何在当前exe中如何将另一个EXE作为资源加载,并执行它。

__inline VOID GetAppPath(LPTSTR pszBuf)
{
    DWORD dwLen = 0;
    if(0 == (dwLen = ::GetModuleFileName(NULL,pszBuf,MAX_PATH)))
    {
        printf("获取APP路径失败,错误码0x%08x\n",GetLastError());
        return;
    }
    DWORD i = dwLen;
    for(; i > 0; i -- )
    {
        if( '\\' == pszBuf[i] )
        {
            pszBuf[i + 1] = '\0';
            break;
        }
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    HMODULE hModule = GetModuleHandle(NULL);
    HRSRC hRsrc = FindResource(hModule, MAKEINTRESOURCE(IDR_RCDATA1), RT_RCDATA);
    if (INVALID_HANDLE_VALUE == hRsrc)
    {
        printf("加载自定义资源失败!\n");
        return 0;
    }

    HGLOBAL hGlobalRes = LoadResource(hModule, hRsrc);
    LPVOID pResMem = LockResource(hGlobalRes);
    DWORD dwSize = SizeofResource(hModule, hRsrc);
    if (NULL == pResMem)
    {
        printf("获取资源所在内存失败!\n");
        return 0;
    }

    TCHAR szFilePath[MAX_PATH] = _T("");
    GetAppPath(szFilePath);
    StringCchCat(szFilePath, MAX_PATH, _T("test.exe"));
    HANDLE hFile = CreateFile(szFilePath, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, 0, NULL);
    if(!WriteFile(hFile, pResMem, dwSize, &dwSize, NULL))
    {
        printf("写文件失败\n");
        return 0;
    }
    CloseHandle(hFile);

    STARTUPINFO si = {0};
    PROCESS_INFORMATION pi = {0}; 
    CreateProcess(szFilePath, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return 0;
}

为了执行上面的代码需要在该项目工程中新加一个资源,将目标EXE添加到资源中,其中资源文件会多出一行”IDR_RCDATA1 RCDATA “E:\Program\ResourcesDemo\Debug\stdWindow.exe” 在resource.h文件中生成了一个资源对应的ID,做好这些工作,该程序就能正常运行 在上面的代码中,依次调用FindResource、 LoadResource、LockResource,获取资源在进程空间中的地址,并将它对应的物理页面锁定在内存中,不允许其进行内存交换。然后将这个部分的内存写入到文件形成一个新的exe,最后执行这个exe,最终上面的程序编译运行后我们会发现在程序对应的目录下会生成一个test.exe文件。

更新资源

在有的时候需要对程序中的资源进行更新,这种情况下一般是在原始的工程下 更改资源,然后重新编译,但是这个时候用户需要下载新的更新程序,在原始程序比较大的情况下,为了更改一个简单的资源就要重新花大量的时间下载并更新程序,可能有点杀鸡用牛刀的意思,在我们只需要更新程序中的资源的情况下,Windows提供了一种方法。 1. 首先使用BeginUpdateResource建立可执行程序文件模块的更新句柄 2. 使用UpdateResource传入之前的更新句柄,更新资源数据 3. 使用EndUpdateResource函数关闭修改句柄,如果想让整个更改存盘需要将函数的第二个参数传入FALSE,这个参数的意思是是否放弃更新,传入false表示保存更新 下面是一个简单的例子

    HMODULE hModule = GetModuleHandle(NULL);

    //加载资源
    HRSRC hRsrc = FindResource(hModule, MAKEINTRESOURCE(IDI_ICON1), RT_GROUP_ICON);
    if (hRsrc == NULL)
    {
        printf("加载资源失败\n");
        return GetLastError();
    }

    HGLOBAL hIcon = LoadResource(hModule, hRsrc);
    PVOID pIconBuf = LockResource(hIcon);
    int nIconSize = SizeofResource(hModule, hRsrc);

    //更新资源
    HANDLE  hUpdate = BeginUpdateResource(_T("E:\\Program\\ResourcesDemo\\Debug\\stdWindow.exe"), TRUE);
    BOOL bRet = UpdateResource(hUpdate, MAKEINTRESOURCE(RT_GROUP_ICON), MAKEINTRESOURCE(IDI_STDWINDOW), GetUserDefaultLangID(), pIconBuf, nIconSize);
    bRet = EndUpdateResource(hUpdate, FALSE);

    return 0;

枚举资源

枚举资源主要使用函数EnumResourceTypes EnumResourceNames, 和EnumResourceLanguages,这几个函数分别枚举资源类型,名称和语言,在msdn中查找函数的定义发现他们的调用顺序必须是type name language,下面是一个简单的枚举的例子:

BOOL CALLBACK EnumResLangProc(HANDLE hModule, LPCTSTR lpszType, LPCTSTR lpszName, WORD wIDLanguage, LONG_PTR lParam)
{
    printf("\tlanguage :%d\n", wIDLanguage);
    return TRUE;
}

BOOL CALLBACK EnumRe1sNameProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
{
    if ((ULONG)lpszName & 0xffff0000)
    {
        printf("\t名称:%s\n", lpszName);
    }else
    {
        printf("\t名称:%d\n", (USHORT)lpszName);
    }
    return EnumResourceLanguages(hModule, lpszType, lpszName, (ENUMRESLANGPROCW)EnumResLangProc, NULL);
}

BOOL CALLBACK EnumResTypeProc(HMODULE hModule, LPTSTR lpszType,LONG_PTR lParam)
{
    if ((ULONG)lpszType & 0xFFFF0000) 
    {
        printf("类型:%s\n", lpszType);
    }else
    {
        printf("类型:%d\n", (USHORT)lpszType);
    }

    return EnumResourceNames(hModule, lpszType, (ENUMRESNAMEPROCW)EnumRe1sNameProc, NULL);
}

int _tmain(int argc, _TCHAR* argv[])
{
    HMODULE hExe = LoadLibrary(_T("E:\\Program\\ResourcesDemo\\Debug\\stdWindow.exe"));
    if (hExe == NULL)
    {
        printf("加载目标程序出错!\n");
        return GetLastError();
    }
    printf("目标程序中包含以下资源:\n");
    EnumResourceTypes(hExe, EnumResTypeProc, NULL);
    return 0;
}

这段代码有以下几点需要注意: 1. LoadLibrary不仅仅可以用来加载dll,实际上它可以加载任意的PE文件到内存,而GetModuleHandle是在内存中查找已经存在的一个模块的句柄,而我们这个地方这个exe事先并没有加载到内存,所以这里用GetModuleHandle是不能正确加载的,只有使用LoadLibrary 2. 这几个枚举函数都需要一个回调函数,这些函数指针类型可以在msdn中查找到,在VC环境下也定义了这些函数指针,但是不知道为什么在填入函数指针时需要强制转化,否则会报错 3. 资源可以使用字符串表示,也可以使用ID表示,这些回调函数虽说传入的都是枚举到的字符串指针,但是它仍然可能是ID,所以在这不能简单的直接把他们作为字符串使用,需要进行判断,判断的依据是它是否大于65536,因为我们说只有在ID值大于这个时,系统才会将ID作为字符串来使用

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏中国白客联盟

使用sqlmap的osshell不出现盘符的解决办法

做个笔记,dba权限时,使用sqlmap的osshell时,当输入路径时,但是sqlmap迟迟不写入盘符,如图: ? 当然,试过f:\,f:\\,f:/,f...

37260
来自专栏Danny的专栏

机房收费系统(VB.NET)——存储过程实战

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

18750
来自专栏跟着阿笨一起玩NET

在Entity Framework中重用现有的数据库连接字符串

本文转载:http://www.cnblogs.com/dudu/archive/2011/01/29/entity_framework_connection_...

13320
来自专栏木宛城主

ASP.NET MVC 随想录—— 使用ASP.NET Identity实现基于声明的授权,高级篇

在这篇文章中,我将继续ASP.NET Identity 之旅,这也是ASP.NET Identity 三部曲的最后一篇。在本文中,将为大家介绍ASP.NET ...

25180
来自专栏程序员的SOD蜜

PDF.NET(PWMIS数据开发框架)之SQL-MAP目标和规范

SQL-MAP的目标: 集中管理SQL语句,所有SQL语句放在专门的配置文件中进行管理; 通过替换SQL配置文件,达到平滑切换数据库到另外一个数据库,比如从O...

27780
来自专栏林德熙的博客

WPF Process.Start 出现 Win32Exception 异常

如果使用下面的代码启动另一个软件,那么在启动的软件路径不存在时,就会出现异常System.ComponentModel.Win32Exception,没有其他信...

20710
来自专栏MasiMaro 的技术博文

老版VC++线程池

在一般的设计中,当需要一个线程时,就创建一个,但是当线程过多时可能会影响系统的整体效率,这个性能的下降主要体现在:当线程过多时在线程间来回切换需要花费时间,而频...

24930
来自专栏大内老A

“前.NET Core时代”如何实现跨平台代码重用 ——程序集重用

除了在源代码层面实现共享(“前.NET Core时代”如何实现跨平台代码重用 ——源文件重用)之外,我们还可以跨平台共享同一个程序集,这种独立于具体平台的“中性...

21080
来自专栏互联网开发者交流社区

ACCESS数据库基本使用

16440
来自专栏me的随笔

.NET Core中的包、元包与框架

.NET Core是一个由NuGet包组成的平台。一些产品受益于细粒度包的定义,也有一些受益于粗粒度包的定义。为了适应这种二重性,.NET Core平台被分为一...

13540

扫码关注云+社区

领取腾讯云代金券