如何将程序的执行文件和静态加载动态库放在不同的目录
2016-06-01 17:46
585 查看
一般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;
}
有没有一种方法不去手动修改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;
}
相关文章推荐
- LeetCode:Peeking Iterator
- 浅谈ACC测试建模
- iOS UIButton
- 第14周项目3-数组类模板
- restrict:限制指令的出现位置
- nginx修改配置之后语法检查通过但是不生效
- 在cygwin下面编译需要winpcap(libpcap)的工程
- C++的头文件和实现文件分别写什么
- Maqetta
- 三种Android适配器
- js中url中文参数乱码问题
- 第13周项目1:分数类中的运算符重载(4)
- 第10章 C语言及程序设计提高例程-37 操作字符串数组
- Android 自定义 Application 多次调用
- 【HDU 1588】Gauss Fibonacci(矩阵快速幂+二分)
- android中判断EditText的内容是否为空
- HTTP的请求方法OPTIONS
- 生活协奏曲
- 什么是EPEL 及 Centos上安装EPEL(转)
- nginx+tomcat集群负载均衡(实现session复制)