Problem of C++ Embedding DLL into Resource Running Release

Keywords: Windows

I wrote a blog post before. C # Embedding dll into Resource Release . Although there are not so many applications where DLL s are embedded into program resources and then released in C++, compared with C_#, it is necessary to understand the next general process. Combining with my practical thinking of solving such problems in practical work, this paper introduces the most basic solution.

1 Embedding DLL into resources

Some programs may call external DLLs when they are running. Users may accidentally lose these DLLs when they are using them, which may cause the program to not run properly. Therefore, we can consider embedding these DLLs into resources and automatically releasing them to the directory of executable programs (or other directories of environment variables) at startup. This paper takes the DLL file needed for embedded FFmpeg+SDL development as an example, and introduces the tips of C++ program (EXE or DLL) embedding DLL into resources and then running and releasing.
The DLL files that need to be embedded are as follows:

When a new C++ project is created with Visual Studio, a filter named "resource file" is automatically generated.

Right-click - > Add - > Resources - > Customize:

Create a new resource type "dlls" and save it. Then the embedded DLL file is added to the resource file and the corresponding ID is renamed. The results are as follows:

The ID definition of the DLL above is located in the resource.h header file:

resource.h

#define IDR_avcodec_55                  102
#define IDR_avdevice_55                 103
#define IDR_avfilter_4                  104
#define IDR_avformat_55                 105
#define IDR_avutil_52                   106
#define IDR_postproc_52                 107
#define IDR_SDL                         108
#define IDR_swresample_0                109
#define IDR_DLLS9                       110
#define IDR_swscale_2                   110

The mapping relationship between DLL resources and physical paths is stored in the *. rc file. Here is a small suggestion. In order to facilitate the migration of the program, it is better to change the path to a relative path (relative to the path of the project):

*.rc

2 Release DLL files

The whole process of releasing files from resources is not complicated. After the program runs, locate the base address of the program in memory, then load the resource file and release it to the specified path. Below is a release DLL file help class I encapsulated:

releaseHelper.h

#ifndef _RELEASEHELPER_H_
#define _RELEASEHELPER_H_

#include <windows.h>

#define MAX_DLL_PATH 1024

//If the action code is in a DLL project, define the following macro, otherwise comment it out
//#define _USER_RELEASEDLL_

//Release DLL Help Class
class CReleaseDLL
{
public:
    CReleaseDLL();
    ~CReleaseDLL();
    /*
        *function:Release DLL file help function
        *[IN]:
            m_lResourceID:ID of resources
            m_strResourceType:Resource type
            m_strReleasePath:Name of file to be released
        *[OUT]:
            Release successful, return TRUE, otherwise return FALSE
    */
     bool FreeResFile(unsigned long m_lResourceID,const char* m_strResourceType, const char* m_strFileName);
private:
    /*
        *function:Get the base address of the module
        *[IN]:
        *[OUT]:
            Returns the acquired base address
    */
    HMODULE GetSelfModuleHandle();

private:
    //Module base address
    HMODULE m_hModule;
    //Program Current Directory
    char m_filePath[MAX_DLL_PATH];
};

#endif

releaseHelper.cpp

#include "releaseHelper.h"
#include <cstdio>
#include <string.h>
#include <direct.h>
#include <exception>

CReleaseDLL::CReleaseDLL()
{
    this->m_hModule=GetSelfModuleHandle();
    if(m_hModule==NULL)
    {
        throw std::exception("Error:Failure to obtain base address");
    }
    //Get directory
    memset(this->m_filePath,0,MAX_DLL_PATH);
    _getcwd(this->m_filePath,MAX_DLL_PATH);
}

CReleaseDLL::~CReleaseDLL()
{
}

bool CReleaseDLL::FreeResFile(unsigned long m_lResourceID, const char* m_strResourceType, const char* m_strFileName)
{
    //Construct a complete release file path
    char strFullPath[MAX_DLL_PATH]={0};
    sprintf_s(strFullPath,"%s\\%s",this->m_filePath,m_strFileName);
    //Search resources
    HRSRC hResID = ::FindResource(this->m_hModule,MAKEINTRESOURCE(m_lResourceID),m_strResourceType);
    //load resources  
    HGLOBAL hRes = ::LoadResource(this->m_hModule,hResID);
    //Lock-in resources
    LPVOID pRes = ::LockResource(hRes);
    if (pRes == NULL) 
    {
        return FALSE;  
    }
    //Get the size of the resource file to be released 
    unsigned long dwResSize = ::SizeofResource(this->m_hModule,hResID);
    //create a file 
    HANDLE hResFile = CreateFile(strFullPath,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    if (INVALID_HANDLE_VALUE == hResFile)
    {
        return FALSE;  
    }
    return TRUE;
}

HMODULE CReleaseDLL::GetSelfModuleHandle()
{
    try
    {
#ifdef _USER_RELEASEDLL_
        //If the released help class is defined in the DLL, the following method is called to get the base address
        MEMORY_BASIC_INFORMATION mbi;  
        return ((::VirtualQuery((LPCVOID)&CReleaseDLL::GetSelfModuleHandle, &mbi, sizeof(mbi)) != 0)?(HMODULE) mbi.AllocationBase : NULL); 
#else
        //If defined directly in the code of exe itself
        return ::GetModuleHandle(NULL);
#endif
    }
    catch(...)
    {
        return NULL;
    }

}

After initializing the class object, the DLL file can be released by calling the FreeResFile function directly. The point that needs to be explained here is that CReleaseDLL is defined in a dynamic link library for program invocation, and CReleaseDLL is defined directly in the code of the program itself. The two ways of getting the program base address are different, so the _USER_RELEASEDLL_macro is added to the above code to compatible with the two modes.
Following is an example code that calls the above wrapper class to complete DLL release:

main.cpp

#include <iostream>
#include "resource.h"
#include "releaseHelper.h"

using namespace std;

#define PAUSE cout<<"Please Entry Any Code..."<<endl;getchar();

int main()
{
    //Defining Operational Class Objects
    CReleaseDLL releasehelper;

    bool blRes;
    blRes=releasehelper.FreeResFile(IDR_avcodec_55,"dlls","avcodec-55.dll");
    blRes=releasehelper.FreeResFile(IDR_avdevice_55,"DLLS","avdevice-55.dll");
    blRes=releasehelper.FreeResFile(IDR_avfilter_4,"DLLS","avfilter-4.dll");
    blRes=releasehelper.FreeResFile(IDR_avformat_55,"DLLS","avformat-55.dll");
    blRes=releasehelper.FreeResFile(IDR_avutil_52,"DLLS","avutil-52.dll");
    blRes=releasehelper.FreeResFile(IDR_postproc_52,"DLLS","postproc-52.dll");
    blRes=releasehelper.FreeResFile(IDR_SDL,"DLLS","SDL.dll");
    blRes=releasehelper.FreeResFile(IDR_swresample_0,"DLLS","swresample-0.dll");
    blRes=releasehelper.FreeResFile(IDR_swscale_2,"DLLS","swscale-2.dll");

    if(blRes)
    {
        cout<<"DLL Document Release Successful"<<endl;
    }
    else
    {
        cout<<"DLL File Release Failed"<<endl;
    }
    PAUSE;
    return 0;
}

3 postscript

Embedding DLL files into program resources and releasing them has practical significance, which can reduce the laziness of developing programs to external environment. Of course, the above method is not limited to releasing DLL files, in theory, any file can be. The above approach may be intercepted by some anti-virus software, because many malicious programs such as remote Trojans adopt similar technology, which leads to the prohibition of such loading behavior.

Posted by lwq on Fri, 24 May 2019 13:56:31 -0700