简单生活日记 活着,然后去爱。简简单单,做个快乐的小孩!

14May/091

使用WebUI控制µTorrent

实用场景

如果想远程控制µTorrent,又不方便、或者无法使用远程桌面之类工具的时候,可以考虑µTorrent的WebUI。µTorrent的这个特性,对于linux系统,甚至一些嵌入式系统,真的是相当有用。

配置方法

配置方法很简单,可以参看这些教程:

需要注意的是:

我一开始就是没么注意到网址后边还有个gui目录,搞得硬是看了人家教程之后才看到效果。

效果截图

不得不承认,µTorrent的WebUI做得相当不错:

µTorrent的WebUI

Tagged as: , , , 1 Comment
6May/090

找回QQ2009自定义表情

QQ2009表情丢失

由于过度强调性能与用户体验,QQ2009的一些早期版本在非正常退出的情况下,很容易丢失所有的自定义表情。所幸的是,实际上被破坏的只是表情的索引信息,而图片文件往往安然无恙。因此,可以通过一些方法对QQ2009中受损的自定义表情包进行修复。

自定义表情修复方法

通过搜索,找到了如下一些关于修复QQ2009自定义表情的方法:

  1. 使用CFRecovery工具进行修复,具体方法参考此页面
  2. 使用新版本的7Zip进行修复,具体方法参考此页面

以上两种方法达到的最终效果是一样的,动手能力稍强的朋友建议直接用7Zip操作;对电脑不是很熟悉的朋友请使用官方修复工具。

Tagged as: , No Comments
16Apr/090

使用IMAP访问邮箱服务

就不多说IMAP 究竟为何物了,罗列一下支持IMAP的邮箱和客户端吧~

支持IMAP的邮箱

已知的支持IMAP的邮件服务提供商有:

支持IMAP的客户端

已知的支持IMAP的邮件客户端有:

配置方法

Tagged as: , , No Comments
9Apr/090

使用游戏手柄操作影音软件

一直想,要是能躺在床上看电影、听音乐,而不用起身换片、换歌,那该多惬意啊。瞅瞅手边的游戏手柄,量量长度似乎够到床头。于是找啊找,还真找着几款软件。

游戏手柄模拟键盘软件

经过搜索,找到以下两款软件:

配置JoyToKey

以我等懒人来说,Xpadder似乎过于强大,用JoyToKey稍稍配置下,能看片、听音乐就成了:

qqplayer setting

图一、JoyToKey用于QQ影音的配置

foobar setting

图二、JoyToKey用于Foobar的配置

8Apr/090

自定义IE打印样式

使用IE打印过网页的朋友都知道,默认情况下打印出来的页面会在页眉上加上标题和页码,而在页脚上加上URL和日期。

自定义需求

我对IE的默认格式总是有些不爽;而我期望的打印样式是:

  • 页眉上放上文档标题,右对齐;
  • 页脚上放上文档的页码与总面数,右对齐;
  • 仅此页已。

实现方法

经过摸索,按如下投置即可:

ie setting

更多自定义项目

其实,这里可以通过使用下表所示的格式化标记进行更多定制:

键入 要打印
&w 窗口标题
&u 网页地址 (URL)
&d 短日期格式(由“控制面板”中的“区域设置”指定)
&D 长日期格式(由“控制面板”中的“区域设置”指定)
&t 由“控制面板”中的“区域设置”指定的时间格式
&T 24 小时时间格式
&p 当前页号
&P 网页总数
&& 个 & 号 (&)
&b 紧跟在这些字符之后的文本居中打印。
&b&b 紧跟在第一个 "&b" 之后的文本居中打印,跟在第二个 "&b" 之后的文本按右对齐方式打印。
Tagged as: , , No Comments
5Mar/090

Windows系统信息查看工具

大家都知道,在Office等软件的“关于/About”对话框中,都有个“系统信息”的按钮。

点一下试试,弹出一个新窗口,从其表现看应该是呼出了一个新进程:

MSInfo32

这个工具初看起来没啥,细看也没啥。可是当你真的想了解系统信息的时候就有用了。另外,“系统信息”程序的“工具”菜单栏下还集成了其它几个工具的入口,方便使用。

那么,除了点击Office的工具栏之后,还有单独的启动方法么?如果我也想在自己的程序中加这么个“系统信息”按钮该怎么去实现呢?

再次祭出Process Monitor工具,很快便看到一条CreateProcess事件:

Process Info

原来就在这里:

C:\Program Files\Common Files\Microsoft Shared\MSInfo\MSinfo32.exe

Tagged as: , No Comments
2Jan/090

网络工具(之一)

题记

很多时候,你要找一些小东小西,却发现怎么也找不到。等到你已经不需要的时候,这些东西却又自己冒了出来。有时候,在网络空间里也是一样。一些网站、应用或者工具,你现在不需要用,或者没时间玩——于是你路过、漂过,不留痕迹。可等到某天你无聊了、需要了,却又总是找不到。我想试试把链接记在blog上,或许日后会好找一点。

Media Info

Context Magic

2Oct/070

译文:NTFS稀疏文件编程提示~

声明

本文为Flexhex网站 NTFS Sparse Files For Programmers 的中文译稿;本文以“现状”提供,译者eRay Jiang)将尽量保持、但不保证与Flexhex网站的同步更新。另注:经原文作者同意,可以任意转载此文,但必须保留原文(英文文章)的链接

目录

  1. 什么是稀疏文件?
  2. MSDN未描述的“稀疏文件”特性
  3. 编程考量
  4. 相关下载
  5. 补充材料(译者)

什么是稀疏文件?

假定你正在实现一个基于NTFS文件的虚拟磁盘。事实上,此类应用相当普及,众多基于文件的“虚拟加密盘”即是此类应用。

考虑一下,如果一个用户请求“建立一个磁盘”,比如说10GB大小,该怎么应付?你或许有两种选择:要么预分配一块10GB的存储空间,而不管它实际使用了多少;要么实现一种复合存储分配机制,允许基础存储空间动态增大或缩小。

然而这两种做法看上去都有瑕疵。预分配空间的方式显然会浪费掉许多空间;而动态缩减空间的做法似乎会很耗费时间。

幸运的是,Windows 2000以上的系统提供一种更好的方案:稀疏文件。当你需要释放文件中的某一块时,你要做的仅仅是告诉系统,文件中有那么些不再被使用的部份;接下来系统将会释放掉相应的磁盘空间。以这种方式,可以为一个文件分配成百上千GB的空间,而实际上只占用几个KB的物理空间。FlexHex的另一篇教程展示了一个示例:一个250M的(译者注:空闲的)稀疏文件,实际上只占用了64KB的磁盘空间。

稀疏文件示意图

值得一提的是,稀疏文件在一般的应用中也是相当有用的。当一个程序尝试读取一个文件的“空洞”区块时,系统会将读取缓冲区填满零。对于程序来讲,这类操作可以看作是成功读取了一个“0数据块”。

当然,一个“对稀疏文件不敏感”的应用程序(sparse-unaware application,即未使用稀疏文件特性的应用,译者注),能够拥有区分真实数据零和稀疏空洞零的能力,无疑会是一个加分项。想像一下,一个TB的(充满空洞的,译者注)稀疏文件,对于一个“稀疏文件敏感(sparse-aware)”的程序来讲,可以毫不费力地加载、复制和扫描;而一个普通应用则可能要耗费相当长的时间去处理这么一个大文件,或者干脆崩溃了事。

MSDN未描述的“稀疏文件”特性

实际的稀疏文件布局

虽然你可以使用FSCTL_SET_ZERO_DATA控制码声明任意区块为“稀疏的”,然而系统只是把你的声明当作一种“建议”,而不是一定会照此执行。Windows可能会以其自身的方式重新排列稀疏文件的布局( FlexHex的一个FAQ 对此有进一步的解释)。

对于一个压缩文件或者稀疏文件,NTFS会将文件分割成许多叫做“压缩单元”的块。如果一个压缩单元分配的实际空间和单元大小相等,那么磁盘上存放的是非压缩数据。如果分配的实际空间比压缩单元小,则表明存放的是压缩数据。如果一个压缩单元没有对应的磁盘簇,这样一个单元当然是用于标记稀疏零。

一般地,一个“稀疏零”区块总是对齐到相邻的压缩单元边界(如果不是,则表明NTFS将其视为压缩单元,而非稀疏区)。然而问题是:压缩单元到底有多大?不同的文件、或者同一个文件的不同流中,一个压缩单元所使用的磁盘簇数可能是不相同的;然则NTFS似乎总是在一个单元中包含16个簇。考虑到NTFS默认以4KB的簇大小格式化硬盘,则一个压缩单元的大小一插般为64KB。

例如,一个文件包含60000字节大小的稀疏零区块,后边跟着一个字节的真正数据;结果是那个文件中包含有60001字节的数据而没有稀疏零存在(即NTFS将这些0全部写到了实际的存储空间中)。

如果创建一个文件,包含70000字节的零区块,后边再跟一个字节的真正数据,那么整个文件会有一个65536字节长的稀疏零块,后跟(70001-65536)=4465字节的真实数据。

真的可以那么大吗?

Jeffrey Richter和Luis Felipe Cabrera在他们1998年发表的A File System for the 21st Century一文中对于稀疏流有这样的描述:一个流可以拥有多达16 billion billion bytes的数据。其实这并不真实。你可以创建16TB大小的稀疏文件,当且仅当这个稀疏文件仅仅是包含一个大大的稀疏零块,而没有任何实际的数据。哪怕是只要写入一个字节,稀疏文件的大小都会被限制到小得多的地步。确切的大小限制取决于许多因素:系统的可用资源、真实数据的实际布局,甚至I/O请求的顺序。在许多情形下,稀疏文件的大小最大也就几百个GB。如果你爱折腾,那么你大可以用FlexHex编辑器去探寻你系统的限度。

出于安全考虑,建议将最大稀疏文件的大小限定在300-500GB左右;创建更大的稀疏文件极有可能导致“磁盘已满”的系统错误,哪怕你真正写入的数据就那么一丁点。搞笑的是,甚至会在将一个区块标志成“空洞”的时候得到这样的系统错误,虽然这个操作实际的工作是释放物理存储。

这看起来似乎有些奇怪,因为NTFS确实没有任何此类的限制,而编码一个稀疏流中的数据并不比获得一个普通文件中的簇列表更复杂。显然Microsoft并不相信有人会真正需要1TB那么大的单一文件,因而也不关心相应的高效实现。

"640 kilobytes of computer memory ought to be enough for anybody."——哈哈,这句话太经典了。

编程考量

注:为了提高可读性,下面的示例代码没有包含错误处理,请勿直接使用。

Win32 API GetVolumeInformation 返回一组文件系统标记,可以用来判断磁盘是否支持稀疏流。

char szVolName[MAX_PATH], szFSName[MAX_PATH];
DWORD dwSN, dwMaxLen, dwVolFlags;
::GetVolumeInformation("C:\\", szVolName, MAX_PATH, &dwSN,
                       &dwMaxLen, &dwVolFlags, szFSName, MAX_PATH);

if (dwVolFlags & FILE_SUPPORTS_SPARSE_FILES) {
  // File system supports sparse streams
}
else {
  // Sparse streams are not supported
}

测试一个文件是否稀疏文件

为了检查一个文件是否稀疏文件,使用GetFileAttributes, GetFileAttributesEx, 或者 GetFileInformationByHandle 方法。记住,前两种形式的方法返回的都是文件中未命名流的属性(这里涉及到了NTFS多流特性,译者注)。也就是说,如果一个文件中包含一个普通的“主流”(Main Stream)和一个附加的稀疏流,那么GetFileAttributesGetFileAttributesEx 返回的结果都会表明该文件为非稀疏的。如果应用程序同时使用了多流特性和稀疏特性,应该使用GetFileInformationByHandle 方法。

HANDLE hFile = ::CreateFile("C:\\Sparse.dat:alt", GENERIC_READ,
                     0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

BY_HANDLE_FILE_INFORMATION bhfi;
::GetFileInformationByHandle(hFile, &bhfi);
if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) {
  // Sparse stream
}
else {
  // Monolithic stream
}

声明稀疏文件

使用DeviceIoControl 方法,同时应用 FSCTL_SET_SPARSE 控制码来标明一个稀疏文件:

HANDLE hFile = ::CreateFile("C:\\Sparse.dat", GENERIC_WRITE,
                     0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);

DWORD dwTemp;
::DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp, NULL);

如果你没有将一个文件标明为稀疏文件,那么FSCTL_SET_ZERO_DATA 控制码将会使得系统真正地往系统中写入0,而非标志出一块稀疏零区域。

记住:将一个文件标志为稀疏文件是单向操作;无法将一个稀疏文件简单地还原为非稀疏文件,即使文件中没有任何的稀疏区域。将稀疏文件还原为非稀疏文件的唯一方法是重新创建一个文件并复制稀疏文件的内容。

转换文件区域为稀疏零区块

没有什么好说的,指定稀疏零区块的起点和终点地址(是地址,而非Size)即可:

FILE_ZERO_DATA_INFORMATION fzdi;
DWORD dwTemp;

fzdi.FileOffset.QuadPart = uAddress;
fzdi.BeyondFinalZero.QuadPart = uAddress + uSize;
::DeviceIoControl(hFile, FSCTL_SET_ZERO_DATA,
                  &fzdi, sizeof(fzdi), NULL, 0, &dwTemp, NULL);

注意,该操作不会发起真正的I/O操作,也不会像WriteFile那样移动文件指针和设置文件结尾(set the end-of-file pointer)。也就说,如果你想在文件尾端放置稀疏零块,则必须调用 SetFilePointerSetEndOfFile 来执行相应动作;否则 DeviceIoControl 将不起作用。

你也许会问,如果仅仅是设置一个文件尾,而不调用DeviceIoControl 又会怎样呢?例如这样:

::SetFilePointer(hFile, 0x1000000, NULL, FILE_END);
::SetEndOfFile(hFile);

那么我们会在新旧文件尾之间的16MB中看到什么呢?稀疏零?真正的数据零?还是一堆乱码?答案是:稀疏零(当然是在这个文件已经是稀疏文件的情况下,译者注)。也就是说,如果要在一个文件尾创建稀疏零区块,用不着调用DeviceIoControl/FSCTL_SET_ZERO_DATA 操作,直接移动文件指针并设置一个新的文件尾即可。

最后一个值得一提的是:同样可以在一个非稀疏文件中使用 FSCTL_SET_ZERO_DATA 标记。 MSDN 描述道:"It is equivalent to using the WriteFile function to write zeros to a file."  这并非完全正确—— 不像WriteFile, FSCTL_SET_ZERO_DATA 既不影响当前文件指针,也不会影响当前的EOF标记。事实上,哪果你在FSCTL_SET_ZERO_DATA 之后立即调用 WriteFile 那么刚刚标记的稀疏零块将被覆盖。

查询稀疏文件布局

也没有什么好讲,指定要查询的区域,并准备好足够的输出缓冲区即可。下面的示例代码打印出稀疏文件中已经分配的区块:

// The uFileSize variable (either 32- or 64-bit long)
// contains the size of the file being queried.

FILE_ALLOCATED_RANGE_BUFFER queryrange;         // Range to be examined
FILE_ALLOCATED_RANGE_BUFFER ranges[1024];       // Allocated areas info
DWORD nbytes, n, i;
BOOL br;

queryrange.FileOffset.QuadPart = 0;             // File range to query
queryrange.Length.QuadPart = uFileSize;         //   (the whole file)

do {
  // We assume in this example that the only possible error is
  // ERROR_MORE_DATA so there is no need to check the actual error code.

  br = ::DeviceIoControl(hFile, FSCTL_QUERY_ALLOCATED_RANGES,
                         &queryrange, sizeof(queryrange),
                         ranges, sizeof(ranges), &nbytes, NULL);

  // Calculate the number of records returned and print them
  n = nbytes / sizeof(FILE_ALLOCATED_RANGE_BUFFER);
  for (i=0; i<n; i++)
    printf("Address: %I64u\tSize: %I64u\n",
           ranges[i].FileOffset.QuadPart,
           ranges[i].Length.QuadPart);

  // Set starting address and size for the next query
  if (!br && n > 0) {
    queryrange.FileOffset.QuadPart = ranges[n-1].FileOffset.QuadPart +
                                     ranges[n-1].Length.QuadPart;
    queryrange.Length.QuadPart = (LONGLONG)uFileSize -
                                 queryrange.FileOffset.QuadPart;
  }
} while (!br);	// Continue loop if ERROR_MORE_DATA

注意:FSCTL_QUERY_ALLOCATED_RANGES 返回已经分配空间区域的位置,而不是“非零区块”的位置。一个已经分配的区块也可以是包含零的。而该标记关心的仅仅是有没有分配真正的物理空间。

稀疏文件实际大小

为了获知一个稀疏文件的实际大小,即实际占用的物理空间,可行的方法是对已分配的区块大小求和。下边的代码中ranges是区块数组,而n则是区块数:

ULONGLONG uSize = 0;
for (i=0; i<n; i++) uSize += ranges[i].Length.QuadPart;

相关下载

所有内容以“现状”提供,代码可以免费用于商业和非商业用途。在保持完整性的前提下可以任意复制和分发。

sparse.zip - 一个稀疏文件复制工具的Visual C++ 源代码,这个工具启用了多流特性。

(译者)补充材料

24Sep/070

GTalk与MSN互通实践~

饿着肚子折腾了两三个小时,终于完美实现了GTalk与MSN的互通~

互通原理

关于GTalk与MSN互通原理不想说得太多(其实是怕说不清楚^_^),有兴趣的同学可以参考以下文章:

或者自行搜索以下关键字:

互通步骤

依照下列步骤完成GTalk与MSN的互通。

第一步:下载Psi

点击下载:http://psi.affinix.com/ ,完成安装。

第二步:安装语言包(可选)

下载:中文语言包,复制到:C:\Program Files\Psi  (如果将Psi安装在其它目录,请调整该路径)。

第三步:配置Psi

第一次运行Psi,弹出添加账户向导(以后可以点击“主菜单->账户设置”):

添加账户

在上图中“姓名”栏输入任意名字,按“添加”按钮;弹出“Psi:账户属性”框:

账户属性

“Jabber ID”中添入Gmail账号全名;接状输入密码;接下来,切换到“连接”选项页:

账户属性2

选中上图中所有“复选框”,并在“主机”项中填入:talk.google.com, 并确保端口号为:5223;最后点击“存盘”,完成Psi配置。

第四步:登录Psi

点击Psi主界面右上角按钮,并选择“上线”(或者右击账户名,再选择“状态->上线”):

登录PSI

上线之后即可在Psi中看到GTalk账号上的好友,并可与在线好友聊天;

第五步:搜寻Jabber服务

呵呵,先别高兴太早,要记住我们的目标是要与MSN好友互通;接下来,(在上线状态下)点击上图中的“搜寻服务”对话框:

搜寻Jabber服务

在“地址”栏中填入一个Jabber服务器地址(地址信息参见后文说明),点击右边的“浏览按钮”;

第六步:注册MSN服务

待服务查询完毕后,右键点击“MSN”相关服务并选“注册”项:

注册MSN服务

在弹出的“注册”对话框中填入MSN账号和密码:

注册MSN服务2

点击“注册”按钮,稍后会弹出“注册成功”提示。

第七步:搞定收工

至此,MSN账号注册完毕;返回Psi主界面,看到亲切的MSN好友了吧!赶快和他们聊聊吧。

现在,可以关掉Psi了,打开熟悉的GTalk吧;接下来的操作你就自己看着办了!

Jabber服务器

虽然Jabber官方列出了一大堆的Jabber服务器,但是速度合适又提供MSN服务的并不好找。网友们推荐的常用Jabber服务器有:

  • jabbernet.dk
  • jaim.at
  • bgmn.net
  • freelinq.com
  • jabber.cn

朋友们在注册之前可以先ping一下看看连接速度;我现在使用深圳电信的ADSL接入,bgmn.net连接最快、最稳定。

参考链接

Tagged as: , , , No Comments
26Aug/07Off

C++ String Class and More~

字符难题

不管是哪种语言或者开发平台,都会遇到字符串的难题;字符串的难题来自于多方面。

  • 来自于语言的难题:
    • 如,C/C++最初用字符数组表示字符串;
    • 那个'\0'的魔术字符不知道折腾过多少人。
  • 来自于平台的难题:
    • 大家都想做点什么改变困境,于是很多平台都提出自己的解决方案;
    • 于是,偶仅在windows平台上就看到了N种字符串,除了最常用的两三种之外,至今没有搞清谁是谁。
  • 来自于交换的难题:
    • base64、HTML encode,一堆堆的编码,还真搞不太清楚~
  • 来自于现实的难题:
    • 好吧,这个好解释,这个世界的语言真是太丰富了;
    • 为了让说啥啥啥啥的民众都能使得上,ASCII、UNICODE、ISO-XXX 一大堆;
    • 咱中国人就是争气,还搞出来个gb2312。

字符类型

起码有两种常见的字符:

  • char
  • wchar

另外,关于tchar,这里有一小段文字作了介绍:

简要地说,tchar要么是char要么是wchar,具体是什么类型,取决于是否定义宏:_UNICODE。

常用类库

  • STL - std::string/std::wstring
  • MFC - CString

CString是基于tchar的,而且其内部已经作了很多转换,所以使用CString可以很方便地处理tchar或char。

而STL中则显示定义了string和wstring分别对应于char版本和wchar版本。可以使用以下方法简单定义一个TChar版本:

#ifdef _UNICODE
#define tstring wstring
#else
#define tstring string
#endif

呵呵,当然也可以basic_string直接定义tchar版本的string。可是,有必要吗?

相关链接