程序如何找自己
自我定位很重要,不但是对人,对程序来说也一样。如果能知道自己的路径位置,程序就可以通过相对定位找到身边的资源文件,这对于制作无需安装的绿色软件和各类插件很关键。下面列出几种程序的自我定位方法。
Windows API
可利用 Win32 API GetModuleHandleEx 来取得内存地址所在的程序路径。如果传入可执行文件自身的某个函数地址,那就能获得自己的路径。该方法如下,默认是取自身的路径:
std::wstring get_module_path(void* address=NULL)
{
if (! address) {
address = (void*)(&get_module_path);
}
HMODULE handle = NULL;
BOOL const ret = ::GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
//|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
static_cast<wchar_t*>(address),
&handle);
if (ret != 0 && handle != NULL) {
wchar_t path_buffer[MAX_PATH] = {L'\0'};
DWORD const ret = ::GetModuleFileNameW(handle, path_buffer, MAX_PATH);
// We have to free it
::FreeLibrary(handle);
if (0 != ret) {
return path_buffer;
}
}
return L""; // not found
}
这个方法也能在 DLL 中使用,让动态库也能获取自身的位置,方便插件的编写。
Unix 平台
与 Win32 类似,在 POSIX 中 dladdr 函数也可以获取某个地址所在可执行文件的路径:
std::string get_module_path(void* address=NULL)
{
if (! address) {
address = (void*)(&get_module_path);
}
::Dl_info dl_info;
dl_info.dli_fname = 0;
int const ret = ::dladdr(address, &dl_info);
if (0 != ret && dl_info.dli_fname != NULL) {
return dl_info.dli_fname;
}
return "";
}
该方法同样能让 so 动态库找到自身的位置,方便插件的编写。
Python
Python 内置变量 __file__ 的值就是脚本文件位置,很方便。不过一旦脚本被 py2exe 这类程序打包后,`` __file__`` 就不准确了。这时就需要用 sys.argv[0] 甚至 sys.executable 变量来取得打包后的程序文件的位置
import imp
import os
import sys
import os.path
def is_frozen():
return (hasattr(sys, "frozen") or # new py2exe
hasattr(sys, "importers") # old py2exe
or imp.is_frozen("__main__")) # tools/freeze
def get_self_path():
if is_frozen():
return os.path.abspath(sys.executable)
return os.path.abspath(sys.argv[0])
Mac & iOS
Mac 和 iOS 平台上标准的 App 结构都是把可执行文件和资源文件打包在程序束 (bundle) 中,苹果还提供专门的 API 来取得 bundle 的路径,从这点上来说,苹果上的软件都是绿色的。不过如果需要,你还是可以取得自身的路径,
char path[1024];
uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0) {
printf("executable path is %s\n", path);
}
else {
printf("buffer too small; need size %u\n", size);
}
这种方法不管是对传统的 Unix 可执行文件或是 Apple 自己标准的 App 都适用。
Mozilla Gecko
Firefox 的扩展都基于 Gecko 架构提供的插件机制。要得到扩展自身的路径应该很方便才是,但实际上并非如此:
Components.utils.import("resource://gre/modules/AddonManager.jsm");
AddonManager.getAddonByID("your@addon.name", function(addon)
{
var uri = addon.getResourceURI("relative/path/to/file");
if (uri instanceof Components.interfaces.nsIFileURL)
{
// get the absolute path to the file inside Your@Addon.name
var absolute_file_path = uri.file.path;
}
});
这里,需要按扩展的名字去 AddonManager 中找出扩展对象,再通过扩展对象取得扩展包内相对位置的文件路径,使用起来不太方便。
其实,Gecko 中有更直接的方法。你可以在 manifest 文件里把需要引用的文件都声明成 resource,所有的 resource 都可以在扩展里通过 URI 名字直接获得路径
resource YOUR-ADDON-LIB path/to/libaddon.so ABI=Linux_x86-gcc3
resource YOUR-ADDON-LIB path/to/addon.dll ABI=WINNT_x86-msvc
const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var uri = ioService.newURI('resource://YOUR-ADDON-LIB', null, null);
if (uri instanceof Components.interfaces.nsIFileURL) {
var lib = ctypes.open(uri.file.path);
/// ...
}
在不同平台上,同一名字还能对应不同的资源文件,方便跨平台扩展的开发。在上面例子中,Win32 平台 js-ctypes 会使用 addon.dll 而 Linux 上则会使用 libaddon.so,这一切都由 gecko 帮着选择定位。我在 chmfox 里正是使用了这个手法。