如何將程序的執行文件和靜態加載動態庫放在不同的目錄

一般windows程序的exe和dll需要放在同一個目錄,靜態加載纔不會報錯,否則需要修改path環境變量,將所有沒有和exe放在同一目錄的dll的路徑加在path環境變量中。

有沒有一種方法不去手動修改path環境變量並且可以將exe和dll隨心所欲的改變路徑呢?我沒有發現,但是我們可以將修改環境變量這件事情交給我們的程序本身來處理,那麼從現象上來看就是我們不需要修改環境變量而可以將dll從exe目錄中拿走,放到你所希望的位置。

其實實現這個想法不難,反而很簡單。

假設我們的exe模塊叫“A.exe”,依賴“B.dll”和“C.dll",你希望將“B.dll”放在”./B“目錄下,而把"C.dll"放在”./C"目錄下。我們的處理思路是,再寫一個殼程序,假如說叫“E.exe”,“E.exe”除了系統的庫之外不依賴任何自己開發或者第三方的庫,那麼理論上它放在哪裏都是可以啓動的。

在“E.exe”的代碼中,我們設置環境變量,將“B.dll”和“C.dll"所在的路徑加到Path環境變量中,並啓動“A.exe”,那麼在啓動“A.exe”時就能自然而然加載到所依賴的庫了。在啓動“A.exe”完成之後將Path環境變量還原。所以在啓動“A.exe”前後,Path環境變量看似並沒有改變,但是我們棘手的問題卻解決了。

當然爲了避免用戶手動去點擊“A.exe”彈出錯誤窗口的不好的用戶體驗,可以將“A.exe”編譯成dll,提供一個能夠啓動程序功能的導出API,在“E.exe”中動態加載“A.dll”並調用導出API,達到啓動程序的目的。

但是exe重編程dll可能引起的問題有很多,比如MFC中就會存在很多的坑,那麼還有一種簡單的思路,那就是直接改名,將編譯完成的“A.exe”直接改名成“A.dll”,然後在“E.exe”中通過CreateProcess的方式啓動“A.dll”,然後就沒“E.exe”什麼事,可以退出歇着去了。

下面是我自己使用的“E.exe”的實現代碼,相當簡單,值得注意的是,“E.exe”運行時最好不要出現窗口,不然會很難看,至於怎麼讓程序不出現窗口,應該網上可以找到很多教程。

//E.exe Main.cpp

class ChangePath
{
public:
	ChangePath()
	{
		size_t len = 0;
		char sz[2048] = {};
		getenv_s(&len, sz,2048,"PATH");
		m_szPath = sz;
		std::string szAddPath = "";//你的dll所在的絕對路徑,使用“;”隔開
		std::string szNewPath = m_szPath + ";";
		szNewPath += szAddPath;
		_putenv_s("PATH",szNewPath.c_str());
	}
	~ChangePath()
	{
		_putenv_s("PATH",m_szPath.c_str());
	}
private:
	std::string m_szPath;
};

ChangePath changepath;//RAII修改Path環境變量

void LoadInstance(void *param)
{
#if 1//假dll
	USES_CONVERSION;
	char apppath[1024] = {};
	std::string exename = "C:/A.dll";
	strcpy_s(apppath,exename.c_str());

	char commandline[2048] = {};
	strcat_s(commandline,_countof(commandline),apppath);
	LPWSTR pszCmdLinew = GetCommandLineW();
	int argc = 0;
	CString FilePath = _T("");
	LPWSTR *argv = CommandLineToArgvW(pszCmdLinew, &argc);
	if (argv != NULL)
	{
		for (int i = 1; i < argc; ++i)
		{
			strcat_s(commandline,_countof(commandline)," ");
			strcat_s(commandline,_countof(commandline),W2A(argv[i]));
		};
		LocalFree(argv);
	}
	PROCESS_INFORMATION pi;
	STARTUPINFOA si = {sizeof(si)};
	CreateProcessA(apppath,commandline,NULL,NULL, FALSE, 0, NULL, NULL, &si, &pi);
#else//真dll
	typedef void (* InvokFunc)();//定義函數指針類型

	HINSTANCE hInst;

	hInst=LoadLibrary(_T("A.dll"));//動態加載Dll

	int error = GetLastError();
	InvokFunc invokFunc=(InvokFunc)GetProcAddress(hInst,"Entrance");//獲取Dll的導出函數

	error = GetLastError();

	if(invokFunc)
	{
		invokFunc();
	}

	::FreeLibrary(hInst);//釋放Dll函數
#endif
}

int main()
{
	LoadInstance(NULL);
	return 1;
}


發佈了33 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章