伤不起的多音字

先从一段代码开始吧。这段代码来自于某个版本的灵图天行者,由于年代久远,所以我并不能保证它完全一致。不过,代码的用意和精髓我还是能够保证忠实还原的。

1
2
3
4
5
6
7
8
9
10
int __cdecl foo(const void* p1, const void* p2)
{
    PCWSTR pwz1 = (PCWSTR)p1;
    PCWSTR pwz2 = (PCWSTR)p2;
    if (0 == wcscmp(pwz1, L"重庆"))
        pwz1 = L"冲庆";
    if (0 == wcscmp(pwz2, L"重庆"))
        pwz2 = L"冲庆";
    return wcscmp(pwz1, pwz2);
}

简单解释一下,这是一个回调函数,被传递给 CRT 的 qsort 函数,用于对省级行政区进行名称排序。代码的用意也很简单,当传入的字符串参数是“重庆”时,以“冲庆”代替之进行比较。
因为“重庆”的“重”是多音字。在普通的字符串之中,被按“zhong”处理了,如果直接排序的话,最后得到的结果将是错误的。考虑如下代码:

int x = wcscmp(L"重庆", L"河北"); // wcscmp returns 1

在我看来,虽然 foo 这个函数可以完成正确的排序,但未免不够优雅。但似乎又没有什么更好的解决方案,于是 foo 的做法保留了很长时间——2009 年后也许有了更好的处理方法,不过我不知道。而且由于我后来离开了 GIS 这个行业,所以逐渐淡忘了这个事情。

不过,一个偶然的机会,iOS 的通讯录给了我一个更好的答案。

如上图所示,一名曾姓童鞋被归入了 C 的索引,有如重庆之于 zhong 庆。不过,通讯录却为用户提供了一个解决方案:

简单来说,为记录增加了一个排序参考字段。如果该字段非空,则以该字段为排序标准,否则以名称为排序标准。问题得以解决。

Categories: 技术的角落 | 3 Comments

LDirScanner:非递归遍历文件夹

久不发码了。今天发个习作,非递归遍历文件夹。
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <atlbase.h>
#include "DirScanner.hpp"
 
void WINAPI OnFile(PCTSTR ptzFullPath, const WIN32_FIND_DATA& wfd, LPARAM)
{
    ATLTRACE(_T("File: %s\n"), ptzFullPath);
}
 
int main(void)
{
    LDirScanner<TCHAR> ds;
    ds.DoScan(_T("d:\\temp\\"), OnFile, 0);
    return 0;
}

完整的 DirScanner.hpp 在这里

Categories: 技术的角落 | Tags: , | 7 Comments

一句话(24)

今天教师节,唱个反调吧。我上高中时与班主任不对眼,她看不上我我也看不上她。当时很多人说:等十年后你就知道老师这么着是为你好了。但十年后的今天,我还是看不上她。也许又有人让我再等十年?好吧,我可以等,不过请不要低估未成年人的情商。卡扎菲都撒丫子跑路了,更何况你们那保守的教育方式呢。

Categories: 随感录 | Tags: | Leave a comment

获取特殊快捷方式的指向路径

在通常的情况下,为了获取一个快捷方式 (.lnk) 指向的路径,可以通过调用 IShellLink 接口的 GetPath 方法来实现。但是,某些特殊的快捷方式是无法取得指向路径的,比如 Office 系列的快捷方式,如下图。

首先你可以看到,普通快捷方式中用于显示路径信息的“目标”一栏已经被禁用掉了。而且,对其调用 IShellLink::GetPath,会得到类似这样的结果:

C:\Windows\Installer\{90120000-0012-0000-0000-0000000FF1CE}\wordicon.exe

我的直觉告诉我,问题的真正答案就藏在 IShellLink 之中。不过,那正确的路是哪一条呢?MSDN 的优点是完备,缺点则是过于完备,我已经数次被淹没其中了。故不取之。

那么还是自己分析吧。我打开 Process Monitor,设置过滤器,如下图。

接下来使用快捷方式来启动 Word,可以看到系统的确访问了 wordicon.exe。

继续往下找,找到 WINWORD.EXE 的第一个现身处。

查看其调用堆栈,如下所示。

从堆栈中,可以得到两个有用的信息:

  1. WINWORD.EXE 真实路径的获取与 CShellLink::_Resolve 有关。而 IShellLink 之中,也存在一个名为 Resolve 的接口。
  2. WINWORD.EXE 真实路径的获取借助了 msi.dll (Microsoft Windows Installer) 的部分函数,而 IShellLink::Resolve 方法的 fFlags 参数恰有一个标志名为 SLR_INVOKE_MSI。

好了,现在已经猜出了那句最重要的代码,可以堆码一试了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CComPtr<IShellLink> sl;
sl.CoCreateInstance(CLSID_ShellLink);
 
// Load the link file...
 
TCHAR sz[MAX_PATH];
WIN32_FIND_DATA wfd;
sl->Resolve(NULL, SLR_INVOKE_MSI);
 
// C:\Windows\Installer\{90120000-0012-0000-0000-0000000FF1CE}\wordicon.exe
sl->GetPath(sz, MAX_PATH, &wfd, SLGP_RAWPATH);
 
// D:\Program Files\Microsoft Office\Office12\WINWORD.EXE
LPITEMIDLIST pidl = NULL;
sl->GetIDList(&pidl);
SHGetPathFromIDList(pidl, sz);
Categories: 技术的角落 | Tags: , , | 3 Comments

一句话(23)

我想借用鲁迅先生《社戏》的一段文字来评价《变形金刚 3》:“于是看小旦唱,看花旦唱,看老生唱,看不知什么角色唱,看一大班人乱打,看两三个人互打,从九点多到十点,从十点到十一点,从十一点到十一点半,从十一点半到十二点。”

Categories: 随感录 | Tags: , | 1 Comment