-
Notifications
You must be signed in to change notification settings - Fork 0
/
how-program-find-self.html
212 lines (204 loc) · 19.6 KB
/
how-program-find-self.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
<!DOCTYPE html>
<!--[if IE 8]> <html class="no-js lt-ie9" lang="zh-cn"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="zh-cn"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>道可叨 | 程序如何找自己 </title>
<meta name="viewport" content="width=device-width">
<meta name="author" content="Qiang">
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<link href="http://localhost:8000/feeds/atom.xml" type="application/atom+xml" rel="alternate" title="道可叨 Atom Feed" />
<link rel="stylesheet" href="https://zhuoqiang.github.io/theme/css/app.css">
<script src="https://zhuoqiang.github.io/theme/js/vendor/custom.modernizr.js"></script>
</head>
<body>
<header>
<hgroup>
<a href="https://zhuoqiang.github.io">
<h1 i18n>道可叨</h1>
<h2>Free Will</h2>
</a>
</hgroup>
</header>
<section>
<article>
<header>
<h1><a href="https://zhuoqiang.github.io/how-program-find-self.html">程序如何找自己</a></h1>
</header> <div>
<section>
<p>自我定位很重要,不但是对人,对程序来说也一样。如果能知道自己的路径位置,程序就可以通过相对定位找到身边的资源文件,这对于制作无需安装的绿色软件和各类插件很关键。下面列出几种程序的自我定位方法。</p>
<div class="contents local topic" id="id2">
<ul class="simple">
<li><a class="reference internal" href="#windows-api" id="id3">Windows API</a></li>
<li><a class="reference internal" href="#unix" id="id4">Unix 平台</a></li>
<li><a class="reference internal" href="#python" id="id5">Python</a></li>
<li><a class="reference internal" href="#mac-ios" id="id6">Mac & iOS</a></li>
<li><a class="reference internal" href="#mozilla-gecko" id="id7">Mozilla Gecko</a></li>
</ul>
</div>
<div class="section" id="windows-api">
<h2><a class="toc-backref" href="#id3">Windows API</a></h2>
<p>可利用 Win32 API <tt class="docutils literal">GetModuleHandleEx</tt> 来取得内存地址所在的程序路径。如果传入可执行文件自身的某个函数地址,那就能获得自己的路径。该方法如下,默认是取自身的路径:</p>
<div class="highlight"><pre><span></span><span class="n">std</span><span class="o">::</span><span class="n">wstring</span> <span class="n">get_module_path</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">address</span><span class="o">=</span><span class="nb">NULL</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="n">address</span><span class="p">)</span> <span class="p">{</span>
<span class="n">address</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)(</span><span class="o">&</span><span class="n">get_module_path</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">HMODULE</span> <span class="n">handle</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">BOOL</span> <span class="k">const</span> <span class="n">ret</span> <span class="o">=</span> <span class="o">::</span><span class="n">GetModuleHandleExW</span><span class="p">(</span>
<span class="n">GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS</span><span class="p">,</span>
<span class="c1">//|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,</span>
<span class="k">static_cast</span><span class="o"><</span><span class="kt">wchar_t</span><span class="o">*></span><span class="p">(</span><span class="n">address</span><span class="p">),</span>
<span class="o">&</span><span class="n">handle</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">&&</span> <span class="n">handle</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">wchar_t</span> <span class="n">path_buffer</span><span class="p">[</span><span class="n">MAX_PATH</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="sa">L</span><span class="sc">'\0'</span><span class="p">};</span>
<span class="n">DWORD</span> <span class="k">const</span> <span class="n">ret</span> <span class="o">=</span> <span class="o">::</span><span class="n">GetModuleFileNameW</span><span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="n">path_buffer</span><span class="p">,</span> <span class="n">MAX_PATH</span><span class="p">);</span>
<span class="c1">// We have to free it</span>
<span class="o">::</span><span class="n">FreeLibrary</span><span class="p">(</span><span class="n">handle</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="mi">0</span> <span class="o">!=</span> <span class="n">ret</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">path_buffer</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="sa">L</span><span class="s">""</span><span class="p">;</span> <span class="c1">// not found</span>
<span class="p">}</span>
</pre></div>
<p>这个方法也能在 DLL 中使用,让动态库也能获取自身的位置,方便插件的编写。</p>
</div>
<div class="section" id="unix">
<h2><a class="toc-backref" href="#id4">Unix 平台</a></h2>
<p>与 Win32 类似,在 POSIX 中 <tt class="docutils literal">dladdr</tt> 函数也可以获取某个地址所在可执行文件的路径:</p>
<div class="highlight"><pre><span></span><span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">get_module_path</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">address</span><span class="o">=</span><span class="nb">NULL</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="n">address</span><span class="p">)</span> <span class="p">{</span>
<span class="n">address</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)(</span><span class="o">&</span><span class="n">get_module_path</span><span class="p">);</span>
<span class="p">}</span>
<span class="o">::</span><span class="n">Dl_info</span> <span class="n">dl_info</span><span class="p">;</span>
<span class="n">dl_info</span><span class="p">.</span><span class="n">dli_fname</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">int</span> <span class="k">const</span> <span class="n">ret</span> <span class="o">=</span> <span class="o">::</span><span class="n">dladdr</span><span class="p">(</span><span class="n">address</span><span class="p">,</span> <span class="o">&</span><span class="n">dl_info</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="mi">0</span> <span class="o">!=</span> <span class="n">ret</span> <span class="o">&&</span> <span class="n">dl_info</span><span class="p">.</span><span class="n">dli_fname</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">dl_info</span><span class="p">.</span><span class="n">dli_fname</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="s">""</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>该方法同样能让 so 动态库找到自身的位置,方便插件的编写。</p>
</div>
<div class="section" id="python">
<h2><a class="toc-backref" href="#id5">Python</a></h2>
<p>Python 内置变量 <tt class="docutils literal">__file__</tt> 的值就是脚本文件位置,很方便。不过一旦脚本被 py2exe 这类程序打包后,`` __file__`` 就不准确了。这时就需要用 <tt class="docutils literal">sys.argv[0]</tt> 甚至 <tt class="docutils literal">sys.executable</tt> 变量来取得打包后的程序文件的位置</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">imp</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">os.path</span>
<span class="k">def</span> <span class="nf">is_frozen</span><span class="p">():</span>
<span class="k">return</span> <span class="p">(</span><span class="nb">hasattr</span><span class="p">(</span><span class="n">sys</span><span class="p">,</span> <span class="s2">"frozen"</span><span class="p">)</span> <span class="ow">or</span> <span class="c1"># new py2exe</span>
<span class="nb">hasattr</span><span class="p">(</span><span class="n">sys</span><span class="p">,</span> <span class="s2">"importers"</span><span class="p">)</span> <span class="c1"># old py2exe</span>
<span class="ow">or</span> <span class="n">imp</span><span class="o">.</span><span class="n">is_frozen</span><span class="p">(</span><span class="s2">"__main__"</span><span class="p">))</span> <span class="c1"># tools/freeze</span>
<span class="k">def</span> <span class="nf">get_self_path</span><span class="p">():</span>
<span class="k">if</span> <span class="n">is_frozen</span><span class="p">():</span>
<span class="k">return</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">executable</span><span class="p">)</span>
<span class="k">return</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
</pre></div>
</div>
<div class="section" id="mac-ios">
<h2><a class="toc-backref" href="#id6">Mac & iOS</a></h2>
<p>Mac 和 iOS 平台上标准的 App 结构都是把可执行文件和资源文件打包在程序束 (bundle) 中,苹果还提供专门的 API 来取得 bundle 的路径,从这点上来说,苹果上的软件都是绿色的。不过如果需要,你还是可以取得自身的路径,</p>
<div class="highlight"><pre><span></span><span class="kt">char</span> <span class="n">path</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
<span class="kt">uint32_t</span> <span class="n">size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">path</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_NSGetExecutablePath</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="o">&</span><span class="n">size</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"executable path is %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">path</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"buffer too small; need size %u</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>这种方法不管是对传统的 Unix 可执行文件或是 Apple 自己标准的 App 都适用。</p>
</div>
<div class="section" id="mozilla-gecko">
<h2><a class="toc-backref" href="#id7">Mozilla Gecko</a></h2>
<p>Firefox 的扩展都基于 Gecko 架构提供的插件机制。要得到扩展自身的路径应该很方便才是,但实际上并非如此:</p>
<div class="highlight"><pre><span></span><span class="nx">Components</span><span class="p">.</span><span class="nx">utils</span><span class="p">.</span><span class="kr">import</span><span class="p">(</span><span class="s2">"resource://gre/modules/AddonManager.jsm"</span><span class="p">);</span>
<span class="nx">AddonManager</span><span class="p">.</span><span class="nx">getAddonByID</span><span class="p">(</span><span class="s2">"your@addon.name"</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">addon</span><span class="p">)</span>
<span class="p">{</span>
<span class="kd">var</span> <span class="nx">uri</span> <span class="o">=</span> <span class="nx">addon</span><span class="p">.</span><span class="nx">getResourceURI</span><span class="p">(</span><span class="s2">"relative/path/to/file"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">uri</span> <span class="k">instanceof</span> <span class="nx">Components</span><span class="p">.</span><span class="nx">interfaces</span><span class="p">.</span><span class="nx">nsIFileURL</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// get the absolute path to the file inside Your@Addon.name</span>
<span class="kd">var</span> <span class="nx">absolute_file_path</span> <span class="o">=</span> <span class="nx">uri</span><span class="p">.</span><span class="nx">file</span><span class="p">.</span><span class="nx">path</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
</pre></div>
<p>这里,需要按扩展的名字去 <tt class="docutils literal">AddonManager</tt> 中找出扩展对象,再通过扩展对象取得扩展包内相对位置的文件路径,使用起来不太方便。</p>
<p>其实,Gecko 中有更直接的方法。你可以在 <tt class="docutils literal">manifest</tt> 文件里把需要引用的文件都声明成 <tt class="docutils literal">resource</tt>,所有的 <tt class="docutils literal">resource</tt> 都可以在扩展里通过 URI 名字直接获得路径</p>
<div class="highlight"><pre><span></span><span class="na">resource YOUR-ADDON-LIB path/to/libaddon.so ABI</span><span class="o">=</span><span class="s">Linux_x86-gcc3</span>
<span class="na">resource YOUR-ADDON-LIB path/to/addon.dll ABI</span><span class="o">=</span><span class="s">WINNT_x86-msvc</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="kr">const</span> <span class="nx">ioService</span> <span class="o">=</span> <span class="nx">Cc</span><span class="p">[</span><span class="s2">"@mozilla.org/network/io-service;1"</span><span class="p">].</span><span class="nx">getService</span><span class="p">(</span><span class="nx">Ci</span><span class="p">.</span><span class="nx">nsIIOService</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">uri</span> <span class="o">=</span> <span class="nx">ioService</span><span class="p">.</span><span class="nx">newURI</span><span class="p">(</span><span class="s1">'resource://YOUR-ADDON-LIB'</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">null</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">uri</span> <span class="k">instanceof</span> <span class="nx">Components</span><span class="p">.</span><span class="nx">interfaces</span><span class="p">.</span><span class="nx">nsIFileURL</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">lib</span> <span class="o">=</span> <span class="nx">ctypes</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">uri</span><span class="p">.</span><span class="nx">file</span><span class="p">.</span><span class="nx">path</span><span class="p">);</span>
<span class="c1">/// ...</span>
<span class="p">}</span>
</pre></div>
<p>在不同平台上,同一名字还能对应不同的资源文件,方便跨平台扩展的开发。在上面例子中,Win32 平台 <tt class="docutils literal"><span class="pre">js-ctypes</span></tt> 会使用 <tt class="docutils literal">addon.dll</tt> 而 Linux 上则会使用 <tt class="docutils literal">libaddon.so</tt>,这一切都由 gecko 帮着选择定位。我在 <a class="reference external" href="https://addons.mozilla.org/zh-cn/firefox/addon/chmfox/">chmfox</a> 里正是使用了这个手法。</p>
</div>
</section>
</div>
<footer>
<p>
<time datetime="2012-06-06T19:26:00+08:00" pubdate>2012-06-06</time><a href="https://zhuoqiang.github.io/category/programming.html"><span class="category" i18n>programming</span></a>
</p>
<ul>
<li><a href="https://zhuoqiang.github.io/tag/c.html"><span class="tag" i18n>c++</span></i></a>
<li><a href="https://zhuoqiang.github.io/tag/firefox.html"><span class="tag" i18n>firefox</span></i></a>
<li><a href="https://zhuoqiang.github.io/tag/ios.html"><span class="tag" i18n>ios</span></i></a>
<li><a href="https://zhuoqiang.github.io/tag/python.html"><span class="tag" i18n>python</span></i></a>
</ul>
</footer><link rel="stylesheet" href="https://unpkg.com/gitalk/dist/gitalk.css">
<script src="https://unpkg.com/gitalk@latest/dist/gitalk.min.js"></script>
<div id="gitalk-container"></div>
<script type="text/javascript">
var gitalk = new Gitalk({
clientID: "2f60008869581fd61d19",
clientSecret: "6b303dcf58c04cd35782bae82540921663cbfda8",
repo: "zhuoqiang.github.com",
owner: "zhuoqiang",
admin: ["zhuoqiang"],
id: "how-program-find-self",
title: "程序如何找自己",
perPage: 30,
distractionFreeMode: true,
pagerDirection: "last",
});
gitalk.render('gitalk-container');
</script></article>
</section>
<footer>
<p>@ <a href="mailto:zhuo.qiang@gmail.com"><span i18n>Qiang</span></a> | <span class="heart"><a href="https://github.com/getpelican/pelican">Pelican</a> & <a href="https://bitbucket.org/zhuoqiang/jing">Jing</a></span></p>
</footer>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.3.min.js"></script>
<script>window.jQuery || document.write('<script src="https://zhuoqiang.github.io/theme/js/vendor/jquery-1.8.3.min.js"><\/script>')</script>
<script src="https://zhuoqiang.github.io/theme/js/vendor/moment.min.js"></script>
<script src="https://zhuoqiang.github.io/theme/js/vendor/moment-lang.min.js"></script>
<script src="https://zhuoqiang.github.io/theme/js/vendor/i18next-1.6.0.min.js"></script>
<script src="https://zhuoqiang.github.io/theme/js/main.js"></script>
<script type="text/javascript">
var GoSquared = {};
GoSquared.acct = "GSN-743443-H";
(function(w){
function gs(){
w._gstc_lt = +new Date;
var d = document, g = d.createElement("script");
g.type = "text/javascript";
g.src = "//d1l6p2sc9645hc.cloudfront.net/tracker.js";
var s = d.getElementsByTagName("script")[0];
s.parentNode.insertBefore(g, s);
}
w.addEventListener ?
w.addEventListener("load", gs, false) :
w.attachEvent("onload", gs);
})(window);
</script>
</body>
</html>