1. 程式人生 > >C++嵌入DLL到資源執行釋放的問題

C++嵌入DLL到資源執行釋放的問題

以前寫過一篇博文《C#嵌入dll到資源釋放的問題》。雖然相對於C#,C++中嵌入DLL到程式資源中,然後再釋放出來的應用場合並沒有那麼多,但是還是有必要了解下一般的過程。結合本人在實際工作中解決此類問題的實踐思路,這裡介紹下最基本的一種解決方案。

1 嵌入DLL到資源

有些程式執行的時候,可能呼叫外部的DLL,使用者使用時可能會不小心丟失這些DLL,導致程式無法正常執行,因此可以考慮將這些DLL嵌入到資源中,啟動時自動釋放到可執行程式目錄(或者其他環境變數目錄)。這裡以嵌入FFmpeg+SDL開發所需要的DLL檔案為例,介紹下C++程式(EXE或者DLL)嵌入DLL到資源後再執行釋放的小技巧。
需要嵌入的DLL檔案如下:

1

利用Visual Studio新建C++專案後,會自動生成一個“資原始檔”的篩選器:

2

右鍵->“新增”->“資源”->”自定義”:

3

新建一種新的資源型別“dlls”並儲存。然後將需要嵌入的DLL檔案加入資原始檔中,重新命名相應的ID,結果如下:

4

上面DLL的ID定義位於resource.h標頭檔案中:

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

DLL資源與實際物理路徑的對映關係都儲存在*.rc檔案中。這裡給個小建議,為了方便程式的移植,最好把路徑改成相對路徑(相對於工程的路徑)

*.rc

5

2 釋放DLL檔案

從資源中釋放檔案,整個過程並不複雜。程式執行後,在記憶體中定位程式的基址,然後載入資原始檔,並將其釋放到指定路徑即可。下面是我封裝的一個釋放DLL檔案幫助類:

releaseHelper.h

#ifndef _RELEASEHELPER_H_
#define _RELEASEHELPER_H_

#include <windows.h>

#define MAX_DLL_PATH 1024

//如果操作程式碼位於DLL工程中,就定義下面的巨集,否則註釋掉
//#define _USER_RELEASEDLL_

//釋放DLL幫助類
class CReleaseDLL
{
public:
    CReleaseDLL();
    ~CReleaseDLL();
    /*
        *function:釋放DLL檔案幫助函式
        *[IN]:
            m_lResourceID:資源的ID
            m_strResourceType:資源型別
            m_strReleasePath:需要釋放的檔名稱
        *[OUT]:
            釋放成功,返回TRUE,否則返回FALSE
    */
     bool FreeResFile(unsigned long m_lResourceID,const char* m_strResourceType, const char* m_strFileName);
private:
    /*
        *function:獲取模組的基址
        *[IN]:
        *[OUT]:
            返回獲取的基址
    */
    HMODULE GetSelfModuleHandle();

private:
    //模組基址
    HMODULE m_hModule;
    //程式當前目錄
    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:獲取基址失敗");
    }
    //獲取目錄
    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)
{
    //構造完整釋放檔案路徑
    char strFullPath[MAX_DLL_PATH]={0};
    sprintf_s(strFullPath,"%s\\%s",this->m_filePath,m_strFileName);
    //查詢資源
    HRSRC hResID = ::FindResource(this->m_hModule,MAKEINTRESOURCE(m_lResourceID),m_strResourceType);
    //載入資源  
    HGLOBAL hRes = ::LoadResource(this->m_hModule,hResID);
    //鎖定資源
    LPVOID pRes = ::LockResource(hRes);
    if (pRes == NULL) 
    {
        return FALSE;  
    }
    //得到待釋放資原始檔大小 
    unsigned long dwResSize = ::SizeofResource(this->m_hModule,hResID);
    //建立檔案 
    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_
        //如果釋放的幫助類定義在DLL中,將呼叫下面的方式獲取基址
        MEMORY_BASIC_INFORMATION mbi;  
        return ((::VirtualQuery((LPCVOID)&CReleaseDLL::GetSelfModuleHandle, &mbi, sizeof(mbi)) != 0)?(HMODULE) mbi.AllocationBase : NULL); 
#else
        //如果直接定義在exe本身的程式碼中
        return ::GetModuleHandle(NULL);
#endif
    }
    catch(...)
    {
        return NULL;
    }

}

初始化類物件後,直接呼叫FreeResFile函式就可以完成DLL檔案的釋放。這裡需要說明的一點就是,將CReleaseDLL定義在一個動態連結庫中供程式來呼叫,和直接將CReleaseDLL定義在程式本身的程式碼中,這兩種獲取程式基址的方式是不一樣的,所以上述程式碼中增加了_USER_RELEASEDLL_巨集,用來相容這兩種模式。
下面是呼叫上述封裝類完成DLL釋放的示例程式碼:

main.cpp

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

using namespace std;

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

int main()
{
    //定義操作類物件
    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檔案釋放成功"<<endl;
    }
    else
    {
        cout<<"DLL檔案釋放失敗"<<endl;
    }
    PAUSE;
    return 0;
}

3 後記

把DLL檔案嵌入到程式資源中並執行釋放有一定的現實意義,可以減少開發程式對外部環境的依懶性。當然了,上面的方法不僅僅侷限於釋放DLL檔案,理論上什麼檔案都是可以的。上面的做法可能會被某些防毒軟體攔截,原因是很多遠控木馬等惡意程式採用了類似的技術,導致殺軟禁止了這種載入行為。

相關推薦

C++嵌入DLL資源執行釋放的問題

以前寫過一篇博文《C#嵌入dll到資源釋放的問題》。雖然相對於C#,C++中嵌入DLL到程式資源中,然後再釋放出來的應用場合並沒有那麼多,但是還是有必要了解下一般的過程。結合本人在實際工作中解決此類問題的實踐思路,這裡介紹下最基本的一種解決方案。 1 嵌入DL

C#嵌入dll資源釋放的問題

http://blog.csdn.net/kikaylee/article/details/44536231  有些程式執行的時候,可能呼叫外部的dll,使用者使用時可能會不小心丟失這些dll,導致程式無法正常執行,因此可以考慮將這些dll嵌入到資源中,啟動時自動釋放。對於託管的dll,我們可以用打包

C# 嵌入dll

resolv appdomain his 一個 我們 文件 跟著 完成後 replace   在很多時候我們在生成C#exe文件時,如果在工程裏調用了dll文件時,那麽如果不加以處理的話在生成的exe文件運行時需要連同這個dll一起轉移,相比於一個單獨幹凈的exe,這種形式

C#將DLL嵌入到exe當中

visual 資源 sse .cn 體積 body [] log sin 原文:C#將DLL嵌入到exe當中一、將dll文件加入工程 1.找到dll文件 可以在項目的“引用”下找到dll,並且根據屬性中的“路徑”找到文件所在目錄;也可以使用bin目錄下自動生成(其實就是

關於 C#呼叫CDll,有回撥函式時,只執行一次回撥函式就直接掛掉 的解決方法

錯誤         直接當機,如下圖:           錯誤原因        回撥函式宣告原因,跟堆疊有關係  

C++沒有final或finally,如何解決異常情況下資源釋放問題?

#include "stdafx.h" #include <iostream> class TestA { public: TestA() { std::cout << "TestA" << std::endl; } ~Tes

關於 C#呼叫一個C/C++dll執行時實現多個應用(靜態變數區分) 的解決方法

需求         VS編寫了純C(windows)通訊庫,需要多例項(靜態變數標誌一個例項,達到互相靜態變數不干涉)。   當前問題         dll中使用了全域性static的標

C#語言中的Using語句塊確保資源釋放

    在開發C#的過程中,.NET在物件不再引用之後,清理它們的方法是使用非析構方式。     在C++中,物件只要使用完畢,就會自動呼叫其解構函式。這對於基於資源的類的設計人員是非常好的訊息,因為如果使用者忘記關閉資源,則最好使用解構函式。只要物件使用完畢,就會呼叫C+

【已解決】C#將DLL嵌入到exe當中

System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)         {             string dllName = args.Name.Contains(",

Java執行緒之釋放鎖,釋放資源釋放CPU

多執行緒中的wait與sleep到底誰釋放了鎖 首先,多執行緒中會使用到兩個延遲的函式,wait和sleep。 wait是Object類中的方法,而sleep是Thread類中的方法。 sleep是Thread類中的靜態方法。無論是在a執行緒中呼叫b的sl

C#裡using的作用------釋放資源

很高興今天看到一個可以拿出來分享的知識點,那就是using的使用。 其實關於using的作用,我想大家最多的用在引入名稱空間。:)其實我在這之前也跟大家一樣,不過今天在看一個小例子後,則讓我產生了疑問。好拉,我先把程式碼附上吧。 using System; using Sys

(C#) 銷燬資源釋放記憶體

0. 什麼是資源? .NET 框架中如何訪問資源?所謂的資源就是程式中可利用的資料,譬如:字串、圖片和任何二進位制資料,包括任何型別的檔案。在面向物件的環境中,每一個型別都標識為某些程式所用的資源,要想使用這些資源,必須為相應的型別分配一定的記憶體空間。訪問一個資源需要如下幾

C#監控代碼執行效率

light 運行時 測量 pan 獲取 min mes csharp stopwatch System.Diagnostics.Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); // 開始監視代碼運行時間

C# 引用DLL版本沖突

src 中一 ~~ 嘗試 .html 1.2 title 代碼 fig 已解決,到官網上下載舊key版本,然後再重定向即可。 手動引用兩個版本的DLL錯誤的原因是我 publicKeyToken 大小寫的問題(竟然沒校驗~~) 但我想不明白,這樣搞如果依賴一多的話,甚至那

C++ Boost 學習資源列表

splay style blog 推薦 trac ffffff color border search 文檔書籍下載 Boost Documentation Boost代碼下載 優秀網站導航 Boos

C# 版dll 程序集合並工具

集合 命令 支持 log 基本 ron 其他 依賴 使用 微軟的ILMerge工具。 下載地址:https://www.microsoft.com/en-us/download/details.aspx?id=17630 這個支持將EXE依賴的DLL合並到EXE

C# 引用DLL調用時註釋無法顯示

spa 目錄 log 生成頁面 調用 dll文件 .com 生成 註釋 修改前,調用DLL中的方法沒有註釋: 修改後,調用DLL中的方法有註釋: 解決辦法: 在生成DLL時,將項目屬性的生成頁面中,勾選“XML文檔文件”

C#引用C++的DLL方案(C#調用非托管動態鏈接庫)

方法 str tor urn pcc tcl ges bin log SocketClientInit是C++裏面定義的方法,通過EntryPoint = "[email protected]@[email protected]"指出這個

java 利用jna調用c#的dll

運行 pat bapi services 包含 外部 生成 輸出 64位 一、需求闡述:   如果我們的項目利用c#開發,到了開發後期需要和java組進行合作,其中有一部分業務邏輯利用c#已經code completed,那麽我們可能會

c#解決dll調用的問題

appdomain 問題 net dep ase lan depend class eve 在做一個c#中間程序時,調用第三方的dll安裝路徑“Program Files” 和“Program Files (x86)”的問題,經過一段時間的研究,找到了下面的解決方案: 思