<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Rainux's Journal &#187; Programming_编程 Archives  &laquo; Rainux&#039;s Journal</title>
	<atom:link href="http://rainux.org/tag/programming_%e7%bc%96%e7%a8%8b/feed/" rel="self" type="application/rss+xml" />
	<link>http://rainux.org</link>
	<description>Rubyist of Vimmer</description>
	<lastBuildDate>Sat, 23 Jul 2011 12:09:18 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Exerb——将 Ruby 脚本转换成 Windows 下的可执行(.exe)文件！</title>
		<link>http://rainux.org/exerb%e2%80%94%e2%80%94%e5%b0%86-ruby-%e8%84%9a%e6%9c%ac%e8%bd%ac%e6%8d%a2%e6%88%90-windows-%e4%b8%8b%e7%9a%84%e5%8f%af%e6%89%a7%e8%a1%8cexe%e6%96%87%e4%bb%b6%ef%bc%81</link>
		<comments>http://rainux.org/exerb%e2%80%94%e2%80%94%e5%b0%86-ruby-%e8%84%9a%e6%9c%ac%e8%bd%ac%e6%8d%a2%e6%88%90-windows-%e4%b8%8b%e7%9a%84%e5%8f%af%e6%89%a7%e8%a1%8cexe%e6%96%87%e4%bb%b6%ef%bc%81#comments</comments>
		<pubDate>Thu, 04 Oct 2007 10:32:07 +0000</pubDate>
		<dc:creator>Rainux</dc:creator>
				<category><![CDATA[Programming 编程]]></category>
		<category><![CDATA[Programming_编程]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby GUI]]></category>

		<guid isPermaLink="false">http://www.rainux.org/2007/10/04/180</guid>
		<description><![CDATA[自从关注并使用 Ruby on Rails 以来，也对 Ruby 语言本身很感兴趣，并经常尝试用它写一些日常使用的小工具脚本。虽然自己使用很方便，但要分享给不用 Ruby 的朋友就比较麻烦了。 但是在 JavaEye 的某个帖子里知道 Exerb 后，事情就变得简单了。Exerb 是一个可以把 Ruby 脚本以及其依赖的 Ruby 扩展库转换成 Windows 下的单个的可独立运行的可执行文件的工具（这么长的一句话读起来是不是很有想扁我的冲动？:p），到 Exerb Project 的主页 下载安装包解开并执行里面的 setup.rb 即可完成安装。 用法也很简单，先执行 mkexy your_script.rb，让 mkexy 启动你的脚本并运行，这期间它会自动探测出 your_script.rb 所依赖的类库文件以及扩展库文件，并在 your_script.rb 结束运行后创建一份清单 your_script.exy，然后执行命令 exerb your_script.exy 即可生成 your_script.exe。OK，现在可以轻松地把你的 Ruby 脚本分享给朋友们了。(注1) 看到这里，也许你会跟我一样想，哇哈哈哈，有了这个工具，用 Ruby 来做 GUI 的小工具就变得有意义了啊。没错，对依赖 fxruby、wxruby 的 Ruby GUI 脚本也能正常的使用 Exerb 完成转换 [...]]]></description>
			<content:encoded><![CDATA[<p>自从关注并使用 Ruby on Rails 以来，也对 Ruby 语言本身很感兴趣，并经常尝试用它写一些日常使用的小工具脚本。虽然自己使用很方便，但要分享给不用 Ruby 的朋友就比较麻烦了。</p>

<p>但是在 <a href="http://www.javaeye.com">JavaEye</a> 的某个帖子里知道 Exerb 后，事情就变得简单了。Exerb 是一个可以把 Ruby 脚本以及其依赖的 Ruby 扩展库转换成 Windows 下的单个的可独立运行的可执行文件的工具（这么长的一句话读起来是不是很有想扁我的冲动？:p），到 <a href="http://exerb.sourceforge.jp/index.en.html">Exerb Project 的主页</a> 下载安装包解开并执行里面的 setup.rb 即可完成安装。</p>

<p>用法也很简单，先执行 <code>mkexy your_script.rb</code>，让 mkexy 启动你的脚本并运行，这期间它会自动探测出 <code>your_script.rb</code> 所依赖的类库文件以及扩展库文件，并在 <code>your_script.rb</code> 结束运行后创建一份清单 <code>your_script.exy</code>，然后执行命令 <code>exerb your_script.exy</code> 即可生成 <code>your_script.exe</code>。OK，现在可以轻松地把你的 Ruby 脚本分享给朋友们了。(注1)</p>

<p>看到这里，也许你会跟我一样想，哇哈哈哈，有了这个工具，用 Ruby 来做 GUI 的小工具就变得有意义了啊。没错，对依赖 fxruby、wxruby 的 Ruby GUI 脚本也能正常的使用 Exerb 完成转换 (注2) (注3)。但是，由于 Exerb 会把 fxruby/wxruby 的运行库嵌入到 .exe 文件里，生成的 .exe 文件都非常大，fxruby 的在 9MB 左右，wxruby 的 11MB 左右。对于一个功能简单的小工具来说，这个程度的体积显然是很多人不能接受的。</p>

<p>事实上，已经有了一个 GUI toolkit 运行库很小，<a href="http://www.osk.3web.ne.jp/~nyasu/vruby/vrproject-e.html">VisualuRuby</a>，虽然它是 Windows Only 的，虽然它现在在 Vista 上还有点小问题。不过这又是另外一个话题了，我会在下一篇 blog 里继续聊。</p>

<p>注1: 在这之前也许你需要用 upx 压缩一下生成的 .exe 文件，因为它确实比较大，一个最简单的 Hello world 也有 1.4MB 左右。</p>

<p>注2: Tk/Gtk/FLTK 我没有试过，对 Tk 不感兴趣，Gtk 在 Windows 下的运行库太过庞大，FLTK 则是因为找到的 ruby-fltk 在 Windows 下的二进制发行版依赖的 Ruby 版本太老，源代码发行版还没来得及尝试自己编译。</p>

<p>注3: 对于 GUI 脚本，使用 <code>exerb -c gui your_script.exy</code> 可以避免生成的可执行文件运行时带有 Windows 命令行窗口。</p>

	标签：<a href="http://rainux.org/tag/programming_%e7%bc%96%e7%a8%8b/" title="Programming_编程" rel="tag">Programming_编程</a>, <a href="http://rainux.org/tag/ruby/" title="Ruby" rel="tag">Ruby</a>, <a href="http://rainux.org/tag/ruby-gui/" title="Ruby GUI" rel="tag">Ruby GUI</a><br />
]]></content:encoded>
			<wfw:commentRss>http://rainux.org/exerb%e2%80%94%e2%80%94%e5%b0%86-ruby-%e8%84%9a%e6%9c%ac%e8%bd%ac%e6%8d%a2%e6%88%90-windows-%e4%b8%8b%e7%9a%84%e5%8f%af%e6%89%a7%e8%a1%8cexe%e6%96%87%e4%bb%b6%ef%bc%81/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>使用 Linux shell I/O 重定向简化小程序的日志记录</title>
		<link>http://rainux.org/%e4%bd%bf%e7%94%a8-linux-shell-io-%e9%87%8d%e5%ae%9a%e5%90%91%e7%ae%80%e5%8c%96%e5%b0%8f%e7%a8%8b%e5%ba%8f%e7%9a%84%e6%97%a5%e5%bf%97%e8%ae%b0%e5%bd%95</link>
		<comments>http://rainux.org/%e4%bd%bf%e7%94%a8-linux-shell-io-%e9%87%8d%e5%ae%9a%e5%90%91%e7%ae%80%e5%8c%96%e5%b0%8f%e7%a8%8b%e5%ba%8f%e7%9a%84%e6%97%a5%e5%bf%97%e8%ae%b0%e5%bd%95#comments</comments>
		<pubDate>Sun, 11 Mar 2007 19:04:46 +0000</pubDate>
		<dc:creator>Rainux</dc:creator>
				<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Programming 编程]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[Programming_编程]]></category>

		<guid isPermaLink="false">http://www.rainux.org/2007/03/12/146</guid>
		<description><![CDATA[一些自动执行特定任务的小脚本程序，如果希望能使程序的 stdout/stderr 输出到屏幕的同时也把他们记录到一个日志文件，可以使用 tee 命令结合 shell I/O 重定向来轻松完成: ./a_tiny_script 2&#62;&#38;1 &#124; tee the_log_contain_both_stdout_and_stderr.log 这个命令的精华在于 2>&#38;1，意为让 stderr 使用 stdout 的文件描述符，效果也就是将 stdout 和 stderr 内容合并，并且输出到 stdout 被定向的位置。在这里也就是管道中的 tee 命令的 stdin 上，然后 tee 将得到的输入同时显示在屏幕上和记录到日志文件里。爽吧？ 以前为了在 PHP 脚本里实现这样的功能竟然使用了 ob 系列函数 + 自定义的 output callback 函数，并且这样也只能做到同时显示和记录 stdout，真是晕到死。 标签：bash, GNU/Linux, PHP, Programming_编程]]></description>
			<content:encoded><![CDATA[<p>一些自动执行特定任务的小脚本程序，如果希望能使程序的 stdout/stderr 输出到屏幕的同时也把他们记录到一个日志文件，可以使用 tee 命令结合 shell I/O 重定向来轻松完成:</p>

<pre name="code" class="bash">./a_tiny_script 2&gt;&amp;1 | tee the_log_contain_both_stdout_and_stderr.log
</pre>

<p>这个命令的精华在于 2>&amp;1，意为让 stderr 使用 stdout 的文件描述符，效果也就是将 stdout 和 stderr 内容合并，并且输出到 stdout 被定向的位置。在这里也就是管道中的 tee 命令的 stdin 上，然后 tee 将得到的输入同时显示在屏幕上和记录到日志文件里。爽吧？</p>

<p>以前为了在 PHP 脚本里实现这样的功能竟然使用了 ob 系列函数 + 自定义的 output callback 函数，并且这样也只能做到同时显示和记录 stdout，真是晕到死。</p>

	标签：<a href="http://rainux.org/tag/bash/" title="bash" rel="tag">bash</a>, <a href="http://rainux.org/tag/gnu-linux/" title="GNU/Linux" rel="tag">GNU/Linux</a>, <a href="http://rainux.org/tag/php/" title="PHP" rel="tag">PHP</a>, <a href="http://rainux.org/tag/programming_%e7%bc%96%e7%a8%8b/" title="Programming_编程" rel="tag">Programming_编程</a><br />
]]></content:encoded>
			<wfw:commentRss>http://rainux.org/%e4%bd%bf%e7%94%a8-linux-shell-io-%e9%87%8d%e5%ae%9a%e5%90%91%e7%ae%80%e5%8c%96%e5%b0%8f%e7%a8%8b%e5%ba%8f%e7%9a%84%e6%97%a5%e5%bf%97%e8%ae%b0%e5%bd%95/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AutoUpdateSoftOnSkycn.cmd</title>
		<link>http://rainux.org/autoupdatesoftonskycn-cmd/</link>
		<comments>http://rainux.org/autoupdatesoftonskycn-cmd/#comments</comments>
		<pubDate>Mon, 26 Dec 2005 17:26:40 +0000</pubDate>
		<dc:creator>Rainux</dc:creator>
				<category><![CDATA[Programming 编程]]></category>
		<category><![CDATA[Software 软件]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Programming_编程]]></category>
		<category><![CDATA[Software_软件]]></category>

		<guid isPermaLink="false">http://rainux.sefans.com/blog/?p=115</guid>
		<description><![CDATA[在软件使用上，我一直都是个喜欢追新的人。 不过这么多年来一成不变的浏览器＋软件下载站＋手工浏览下载的模式已经让我厌倦了。懒人总是有懒人的办法，因此也就有了这个使用 Windows 命令行脚本编写的 AutoUpdateSoftOnSkycn.cmd。 AUSOS 的工作原理很简单，用 wget 取得软件下载站上的某个软件的介绍页面，然后使用 grep/sed 来分析页面的文本取得此软件的下载地址，再次使用 wget 取得这个软件。使这个工作流程能真正应用的重点在于，大多数软件站的某个具体软件的介绍页面地址都具有持久性，这是为了提高使用具体关键字时自己在搜索引擎中的排名，同时也方便用户访问和自己管理。 基于这个工作原理，AUSOS 稍加修改就可以适用于大多数软件下载站，实际上这也是我下一步的目标。不过这样一来配置文件 AUSOS.conf 和项目列表文件 ItemIDList.ini 就会复杂一些了，到时候也许会做一个 GUI，如果真的需要的话。 另外编码过程中发现 Win2k 以后 Windows 命令行脚本虽然增强了很多，但还是有些不便，也许以后会移植到 bash 脚本上，这样虽然看上去没那么 NB 了，不过可以做到跨平台。Windows 下用 Cygwin 里的 bash 来执行；Linux 自是不必说，bash 已经流行很多年了。 写完发给一个 coder 朋友看，他说“Windows shell script 都可以写那么长，你丫真素 YD。”，呵呵。 如果你有兴趣的话可以在线看看 AUSOS 在 Vim 中高亮后的代码。并且，使用说明也可以在这里比较方便的看到。 下载地址: AutoUpdateSoftOnSkycn.cmd v0.2 超微型的 Cygwin 环境，如果你和大多数人一样没有安装 Cygwin，那么请下载并解压到 [...]]]></description>
			<content:encoded><![CDATA[<p>在软件使用上，我一直都是个喜欢追新的人。</p>

<p>不过这么多年来一成不变的浏览器＋软件下载站＋手工浏览下载的模式已经让我厌倦了。懒人总是有懒人的办法，因此也就有了这个使用 Windows 命令行脚本编写的 AutoUpdateSoftOnSkycn.cmd。</p>

<p>AUSOS 的工作原理很简单，用 wget 取得软件下载站上的某个软件的介绍页面，然后使用 grep/sed 来分析页面的文本取得此软件的下载地址，再次使用 wget 取得这个软件。使这个工作流程能真正应用的重点在于，大多数软件站的某个具体软件的介绍页面地址都具有持久性，这是为了提高使用具体关键字时自己在搜索引擎中的排名，同时也方便用户访问和自己管理。</p>

<p>基于这个工作原理，AUSOS 稍加修改就可以适用于大多数软件下载站，实际上这也是我下一步的目标。不过这样一来配置文件 AUSOS.conf 和项目列表文件 ItemIDList.ini 就会复杂一些了，到时候也许会做一个 GUI，如果真的需要的话。</p>

<p>另外编码过程中发现 Win2k 以后 Windows 命令行脚本虽然增强了很多，但还是有些不便，也许以后会移植到 bash 脚本上，这样虽然看上去没那么 NB 了，不过可以做到跨平台。Windows 下用 Cygwin 里的 bash 来执行；Linux 自是不必说，bash 已经流行很多年了。</p>

<p>写完发给一个 coder 朋友看，他说“Windows shell script 都可以写那么长，你丫真素 YD。”，呵呵。<img src="/face/023.gif" alt="023" /></p>

<p>如果你有兴趣的话可以在线看看 <a href="/produce/AutoUpdateSoftOnSkycn.cmd.html">AUSOS 在 Vim 中高亮后的代码</a>。并且，使用说明也可以在这里比较方便的看到。</p>

<p>下载地址:<br />
<a href="/produce/AUSOS.7z">AutoUpdateSoftOnSkycn.cmd v0.2</a><br />
<a href="/produce/TinyCygwin.7z">超微型的 Cygwin 环境</a>，如果你和大多数人一样没有安装 Cygwin，那么请下载并解压到 AUSOS 所在目录或者 PATH 环境变量中列出的任何一个目录里。</p>

	标签：<a href="http://rainux.org/tag/programming_%e7%bc%96%e7%a8%8b/" title="Programming_编程" rel="tag">Programming_编程</a>, <a href="http://rainux.org/tag/software_%e8%bd%af%e4%bb%b6/" title="Software_软件" rel="tag">Software_软件</a>, <a href="http://rainux.org/tag/windows/" title="Windows" rel="tag">Windows</a><br />
]]></content:encoded>
			<wfw:commentRss>http://rainux.org/autoupdatesoftonskycn-cmd/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>SCColoredID，星际争霸彩色 ID 修改器 v0.2.0，支持 Windows Vista</title>
		<link>http://rainux.org/sccoloredid-starcraft-colorful-id-trainer/</link>
		<comments>http://rainux.org/sccoloredid-starcraft-colorful-id-trainer/#comments</comments>
		<pubDate>Wed, 12 Oct 2005 12:21:48 +0000</pubDate>
		<dc:creator>Rainux</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Programming 编程]]></category>
		<category><![CDATA[Programming_编程]]></category>

		<guid isPermaLink="false">http://rainux.sefans.com/blog/?p=109</guid>
		<description><![CDATA[我知道早在几年前就有了这种工具。不过我所找到的这类工具都不大好用，比如 W.Z.Q 的那个，每次修改都要从游戏里切换出来，很不方便。因此一怒之下自己写了个，具备全局快捷键修改，以及 ID 列表保存能力。 同时附上 Delphi 源代码。其实程序很简单，就是用一个循环的 ReadProcessMemory() 来读取 starcraft.exe 进程的内存，并且通过搜索一个特征字符串找到游戏的 ID 列表位置，然后用 WriteProcessMemory() 写入我们设置的彩色 ID 而已。至于如何让 ID 以各种颜色显示，则要感谢前人 W.Z.Q 的研究。事实上我是使用 WinHex 查看经他的 scRedStorm 修改过 starcraft.exe 进程的内存得到的这个“秘密”。 更新历史 v0.1.0221.874 解决对 1.08 以上版本无效的低级问题。 v0.2.0 即时预览彩色 ID，精确模拟星际争霸对待彩色代码的行为。 支持 Windows Vista。为了避免与 Vista 的快捷键冲突，以前的 Ctrl + Alt + Tab 已经更改为 Shift + Tab。 ID 首尾都可以使用空格。 放弃 FlatStyle 控件，在 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/produce/SCColoredID.png" alt="SCColoredID 界面" /></p>

<p>我知道早在几年前就有了这种工具。不过我所找到的这类工具都不大好用，比如 W.Z.Q 的那个，每次修改都要从游戏里切换出来，很不方便。因此一怒之下自己写了个，具备全局快捷键修改，以及 ID 列表保存能力。</p>

<p>同时附上 Delphi 源代码。其实程序很简单，就是用一个循环的 ReadProcessMemory() 来读取 starcraft.exe 进程的内存，并且通过搜索一个特征字符串找到游戏的 ID 列表位置，然后用 WriteProcessMemory() 写入我们设置的彩色 ID 而已。至于如何让 ID 以各种颜色显示，则要感谢前人 W.Z.Q 的研究。事实上我是使用 WinHex 查看经他的 scRedStorm 修改过 starcraft.exe 进程的内存得到的这个“秘密”。</p>

<h2>更新历史</h2>

<p>v0.1.0221.874</p>

<ul>
<li>解决对 1.08 以上版本无效的低级问题。</li>
</ul>

<p>v0.2.0</p>

<ul>
<li>即时预览彩色 ID，精确模拟星际争霸对待彩色代码的行为。</li>
<li>支持 Windows Vista。为了避免与 Vista 的快捷键冲突，以前的 Ctrl + Alt + Tab 已经更改为 Shift + Tab。</li>
<li>ID 首尾都可以使用空格。</li>
<li>放弃 FlatStyle 控件，在 Vista 里它已经不再好看了。</li>
</ul>

<h2>下载</h2>

<p><a href="/produce/SCColoredID.exe">SCColoredID v0.2.0</a><br />
<a href="/produce/SCColoredID-src.7z">SCColoredID v0.2.0 源代码</a></p>

	标签：<a href="http://rainux.org/tag/delphi/" title="Delphi" rel="tag">Delphi</a>, <a href="http://rainux.org/tag/programming_%e7%bc%96%e7%a8%8b/" title="Programming_编程" rel="tag">Programming_编程</a><br />
]]></content:encoded>
			<wfw:commentRss>http://rainux.org/sccoloredid-starcraft-colorful-id-trainer/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>发现一个 ACDSee 7.x 可能是因为硬编码造成的问题</title>
		<link>http://rainux.org/acdsee-7x</link>
		<comments>http://rainux.org/acdsee-7x#comments</comments>
		<pubDate>Sun, 16 Jan 2005 20:23:16 +0000</pubDate>
		<dc:creator>Rainux</dc:creator>
				<category><![CDATA[Programming 编程]]></category>
		<category><![CDATA[Software 软件]]></category>
		<category><![CDATA[Programming_编程]]></category>
		<category><![CDATA[Software_软件]]></category>

		<guid isPermaLink="false">http://rainux.sefans.com/blog/?p=65</guid>
		<description><![CDATA[大家都知道 ACDSee 有 Viewer 和 Browser 两种模式。一般来说我们在“我的电脑”或者其他文件管理器里双击一个图片文件时 ACDSee 会以 Viewer 模式启动，以便让我们查看双击的这张图片。此时如果我们双击 ACDSee 的显示图片的区域，会使其切换到 Browser 模式，以便让我们浏览刚才打开的图片所在文件夹下的所有图片。 问题就出在这个 Browser 模式上。 Windows 2000 以上的版本默认把某个用户的注册表文件，各种软件的配置和数据文件等等保存到以该用户的名字命名的个人文件夹里，这个文件夹通常通常位于安装 Windows 的分区的 Documents and Settings 文件夹下。Windows 以环境变量 %USERPROFILE% 来表示它。而我为了让 Windows 彻底崩溃后不用手动去挽救数据，通常会把很多本来位于 %USERPROFILE% 文件夹里的系统特殊文件夹重定义到其他地方。包括 Application Data、桌面等。 结果，在随手删除掉 %USERPROFILE% 里已经无意义的“桌面”文件夹 (因为我已经把它重定义到了其他位置) 后，ACDSee 的 Browser 模式无法正常工作了。表现为由 Viewer 模式切换到 Browser 模式后不会显示出所在文件夹的任何图片文件，或者直接启动 Browser 也不会显示大多数文件夹下的图片文件。 发现造成这个问题的原因浪费了我几天前的一个夜晚几个小时的时间，最后在网友急云的提示下才搞清楚。 或许这只是个很小的，微不足道的，大多数人不会遇到的问题，但至少它对我们软件开发者来说有特别的意义。因为这很可能是 ACDSee 的作者在一定程度上使用了硬编码 (hard [...]]]></description>
			<content:encoded><![CDATA[<p>大家都知道 ACDSee 有 Viewer 和 Browser 两种模式。一般来说我们在“我的电脑”或者其他文件管理器里双击一个图片文件时 ACDSee 会以 Viewer 模式启动，以便让我们查看双击的这张图片。此时如果我们双击 ACDSee 的显示图片的区域，会使其切换到 Browser 模式，以便让我们浏览刚才打开的图片所在文件夹下的所有图片。</p>

<p>问题就出在这个 Browser 模式上。
<span id="more-65"></span>
Windows 2000 以上的版本默认把某个用户的注册表文件，各种软件的配置和数据文件等等保存到以该用户的名字命名的个人文件夹里，这个文件夹通常通常位于安装 Windows 的分区的 Documents and Settings 文件夹下。Windows 以环境变量 %USERPROFILE% 来表示它。而我为了让 Windows 彻底崩溃后不用手动去挽救数据，通常会把很多本来位于 %USERPROFILE% 文件夹里的系统特殊文件夹重定义到其他地方。包括 Application Data、桌面等。</p>

<p>结果，在随手删除掉 %USERPROFILE% 里已经无意义的“桌面”文件夹 (因为我已经把它重定义到了其他位置) 后，ACDSee 的 Browser 模式无法正常工作了。表现为由 Viewer 模式切换到 Browser 模式后不会显示出所在文件夹的任何图片文件，或者直接启动 Browser 也不会显示大多数文件夹下的图片文件。</p>

<p>发现造成这个问题的原因浪费了我几天前的一个夜晚几个小时的时间，最后在网友急云的提示下才搞清楚。</p>

<p>或许这只是个很小的，微不足道的，大多数人不会遇到的问题，但至少它对我们软件开发者来说有特别的意义。因为这很可能是 ACDSee 的作者在一定程度上使用了硬编码 (hard code) 造成的。</p>

<p>很多时候，我们为了方便而使用硬编码。要说是在用于教学或者演示的 demo 里为了方便或者突出重点使用硬编码，那是无可厚非的。但如果由此让自己或者别人养成了使用硬编码的习惯，那就罪大恶极了。</p>

<p>其实，著名的由两位数表示年份造成的“千年虫”问题就是个典型的硬编码带来的灾难。</p>

	标签：<a href="http://rainux.org/tag/programming_%e7%bc%96%e7%a8%8b/" title="Programming_编程" rel="tag">Programming_编程</a>, <a href="http://rainux.org/tag/software_%e8%bd%af%e4%bb%b6/" title="Software_软件" rel="tag">Software_软件</a><br />
]]></content:encoded>
			<wfw:commentRss>http://rainux.org/acdsee-7x/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>收到 Borland China 寄来的 Delphi 2005 试用版光盘</title>
		<link>http://rainux.org/borland-china-delphi-2005</link>
		<comments>http://rainux.org/borland-china-delphi-2005#comments</comments>
		<pubDate>Mon, 27 Dec 2004 08:12:03 +0000</pubDate>
		<dc:creator>Rainux</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Programming 编程]]></category>
		<category><![CDATA[Programming_编程]]></category>

		<guid isPermaLink="false">http://rainux.sefans.com/blog/?p=37</guid>
		<description><![CDATA[虽然早用上了 Delphi 2005 Update 1 正式版，这张光盘已经没有实际意义，但还是挺兴奋的，毕竟是 Borland 寄给我的东西，呵呵。 平心而论 Delphi 2005 是个不错的产品，虽然我们可以找出一堆数落她的理由。比如启动太慢，占用内存太多，IDE 太像 Visual Studio 而失去了 Delphi 传统的风格，编辑器反应比较慢等等。不过在同一个 IDE 里同时支持 Delphi for Win32、Delphi for .NET 以及 C# 确实是个很诱人的卖点。以及新增的 Refactoring、UML Modeling 等等都是很实用很重要的功能。 Delphi 2005 是个不错的产品，但 Borland 对她的宣传确实有些过了。 标签：Delphi, Programming_编程]]></description>
			<content:encoded><![CDATA[<p>虽然早用上了 Delphi 2005 Update 1 正式版，这张光盘已经没有实际意义，但还是挺兴奋的，毕竟是 Borland 寄给我的东西，呵呵。</p>

<p>平心而论 Delphi 2005 是个不错的产品，虽然我们可以找出一堆数落她的理由。比如启动太慢，占用内存太多，IDE 太像 Visual Studio 而失去了 Delphi 传统的风格，编辑器反应比较慢等等。不过在同一个 IDE 里同时支持 Delphi for Win32、Delphi for .NET 以及 C# 确实是个很诱人的卖点。以及新增的 Refactoring、UML Modeling 等等都是很实用很重要的功能。</p>

<p>Delphi 2005 是个不错的产品，但 Borland 对她的宣传确实有些过了。</p>

	标签：<a href="http://rainux.org/tag/delphi/" title="Delphi" rel="tag">Delphi</a>, <a href="http://rainux.org/tag/programming_%e7%bc%96%e7%a8%8b/" title="Programming_编程" rel="tag">Programming_编程</a><br />
]]></content:encoded>
			<wfw:commentRss>http://rainux.org/borland-china-delphi-2005/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于 Delphi 参数传递方式的一点研究</title>
		<link>http://rainux.org/delphi</link>
		<comments>http://rainux.org/delphi#comments</comments>
		<pubDate>Thu, 29 Jul 2004 07:42:30 +0000</pubDate>
		<dc:creator>Rainux</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Programming 编程]]></category>
		<category><![CDATA[Programming_编程]]></category>

		<guid isPermaLink="false">http://rainux.sefans.com/blog/?p=7</guid>
		<description><![CDATA[某次看 D6DG 说默认的参数传递方式因为会为变量产生本地副本所以会消耗额外的内存，而 const 方式会优化字符串和记录类型的参数传递时的内存占用。从而猜测 const 方式的参数传递实际也是按地址传递，只是编译器强制不允许函数内的代码修改 const 方式传递进去的变量而已。 为了证实我的猜想，特设计以下实验，本实验中使用到了字符串(本文提到的字符串都是指 Delphi 默认的字符串 AnsiString)内部结构中的引用计数。 关于字符串的引用计数，结合 D6DG 中的说明和偶的实际研究，得出的结论是，字符串地址实际上是其内容的第一个字符的地址，在此地址之前的 12 字节内存中的内容才是字符串内部结构中的头部，分别是 32 位字符串占用的内容空间大小，32 位引用计数，32 位字符串长度(仅在 Delphi 7 中验证，推测 Delphi 7 以前的 32 位 Delphi 中的字符串结构都应该是这样，未验证)。 以下代码中 sGlobal 为全局字符串变量。 procedure Method1(S: string); var P: PInteger; begin // S := S + 'k'; // 字符串实际上是指针，这里只是将其强制转换为 PInteger 备用 P := PInteger(S); [...]]]></description>
			<content:encoded><![CDATA[<p>某次看 D6DG 说默认的参数传递方式因为会为变量产生本地副本所以会消耗额外的内存，而 const 方式会优化字符串和记录类型的参数传递时的内存占用。从而猜测 const 方式的参数传递实际也是按地址传递，只是编译器强制不允许函数内的代码修改 const 方式传递进去的变量而已。</p>

<p>为了证实我的猜想，特设计以下实验，本实验中使用到了字符串(本文提到的字符串都是指 Delphi 默认的字符串 AnsiString)内部结构中的引用计数。</p>

<p>关于字符串的引用计数，结合 D6DG 中的说明和偶的实际研究，得出的结论是，字符串地址实际上是其内容的第一个字符的地址，在此地址之前的 12 字节内存中的内容才是字符串内部结构中的头部，分别是 32 位字符串占用的内容空间大小，32 位引用计数，32 位字符串长度(仅在 Delphi 7 中验证，推测 Delphi 7 以前的 32 位 Delphi 中的字符串结构都应该是这样，未验证)。</p>

<p>以下代码中 sGlobal 为全局字符串变量。</p>

<pre name="code" class="delphi">procedure Method1(S: string);
var
  P: PInteger;
begin
  // S := S + 'k';
  // 字符串实际上是指针，这里只是将其强制转换为 PInteger 备用
  P := PInteger(S);
  // 得到字符串地址左偏移 8 字节的地址，也就是字符串的 32 位引用计数存储的位置
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('S 引用计数: ' + IntToStr(P^));
  P := PInteger(sGlobal);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('sGlobal 引用计数: ' + IntToStr(P^));
  Form1.Memo1.Lines.Add('S 地址: ' + IntToStr(Integer(Pointer(S))));
  Form1.Memo1.Lines.Add('');
end;

procedure Method2(var S: string);
var
  P: PInteger;
begin
  P := PInteger(S);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('S 引用计数: ' + IntToStr(P^));
  P := PInteger(sGlobal);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('sGlobal 引用计数: ' + IntToStr(P^));
  Form1.Memo1.Lines.Add('S 地址: ' + IntToStr(Integer(Pointer(S))));
  Form1.Memo1.Lines.Add('');
end;

procedure Method3(const S: string);
var
  P: PInteger;
begin
  P := PInteger(S);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('S 引用计数: ' + IntToStr(P^));
  P := PInteger(sGlobal);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('sGlobal 引用计数: ' + IntToStr(P^));
  Form1.Memo1.Lines.Add('S 地址: ' + IntToStr(Integer(Pointer(S))));
  Form1.Memo1.Lines.Add('');
end;

procedure sGlobalInfo;
var
  P: PInteger;
begin
  P := PInteger(sGlobal);
  P := PInteger(Integer(P) - 8);
  Form1.Memo1.Lines.Add('sGlobal 引用计数: ' + IntToStr(P^));
  Form1.Memo1.Lines.Add('sGlobal 地址: ' + IntToStr(Integer(sGlobal)));
  Form1.Memo1.Lines.Add('');
end;
</pre>

<p>在主程序里分别以初始化后的 sGlobal 为实参调用这几个过程后，我们会看到三种方式 S 的地址都跟 sGlobal 的一样，这初步说明 var 方式和 const 方式确实都是按地址传递参数。</p>

<p>但不可思议的是，默认参数传递方式不会是为变量产生本地副本吗？为什么 S 的地址还会跟 sGlobal 一样呢？因为一开始我设计这个实验时并没有添加显示引用计数的代码，所以很是迷茫。之后想起了 Borland 使用了引用计数的方式和 copy-on-write 技术(对于字符串等使用这两个技术的数据类型的变量 A、B，将 A 赋值给 B 时实际上只是赋值 A 的地址给 B，并增加 A 的引用计数，直到两者其中一个被修改时才为 B 申请新的内存空间并且复制 A 的内容，同时减少 A 的引用计数)来优化字符串的操作。于是查阅 D6DG 并且观察 Delphi 的汇编代码和内存(在 CPU 窗口中)得出了本文开头关于字符串引用计数的结论。</p>

<p>这样，添加了显示引用计数的代码之后我们就可以观察到，使用默认方式时 S 和 sGlobal 的引用计数，都是 2，而其他方式时两者的引用计数都是 1，这更有力的说明了 const 方式确实是按地址传递，而不是像默认方式那样只是增加引用计数。这样，当我们把第一个过程中第一行代码的注释去掉后再次运行程序，可以看到默认方式时 S 的地址已经跟 sGlobal 不同了，同时两者的引用计数都是 1 了，说明确实是在 S 被修改后才产生本地副本(copy-on-write)。</p>

<p>那么，现在我们知道参数类型为字符串时， const 方式的参数传递实际是传递参数地址，这可以优化内存的使用，而参数为其他数据类型时是不是这样呢？</p>

<p>为此修改刚才的程序如下，其中 iGlobal 为全局整形变量。</p>

<pre name="code" class="delphi">procedure Method1(I: Integer);
begin
  Form1.Memo1.Lines.Add('I 地址: ' + IntToStr(Integer(@I)));
  Form1.Memo1.Lines.Add('');
end;

procedure Method2(var I: Integer);
begin
  Form1.Memo1.Lines.Add('I 地址: ' + IntToStr(Integer(@I)));
  Form1.Memo1.Lines.Add('');
end;

procedure Method3(const I: Integer);
begin
  Form1.Memo1.Lines.Add('I 地址: ' + IntToStr(Integer(@I)));
  Form1.Memo1.Lines.Add('');
end;

procedure iGlobalInfo;
begin
  Form1.Memo1.Lines.Add('iGlobal 地址: ' + IntToStr(Integer(@iGlobal)));
  Form1.Memo1.Lines.Add('');
end;
</pre>

<p>在主程序里分别以初始化后的 iGlobal 为实参调用这个过程后，可以看到只有 var 方式中 I 的地址跟 iGlobal 一样，而默认方式和 const 都会为 iGlobal 产生本地副本，可见确实如 D6DG 所说 const 会(也只会)优化字符串和记录类型的参数传递时的内存占用。</p>

<p>至此实验目的达到，还附带了解了 string 的内部格式。</p>

<p>附上<a href="/stuff/20040729.Delphi.01.rar">实验程序源代码</a>。</p>

	标签：<a href="http://rainux.org/tag/delphi/" title="Delphi" rel="tag">Delphi</a>, <a href="http://rainux.org/tag/programming_%e7%bc%96%e7%a8%8b/" title="Programming_编程" rel="tag">Programming_编程</a><br />
]]></content:encoded>
			<wfw:commentRss>http://rainux.org/delphi/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

