browser icon
You are using an insecure version of your web browser. Please update your browser!
Using an outdated browser makes your computer unsafe. For a safer, faster, more enjoyable user experience, please update your browser today or try a newer browser.

技术的角落

软件盈利那些事儿

屈指算来,我编写漫画控这个软件已经有两年的时间了。在此,将这两年来所尝试的经验和想法写出来,以供诸君品评。 在中国,纯靠写软件来盈利实在是一件很难的事情。盗版和破解扼杀了零售的可能,木马或病毒又不是问心无愧的收入;思来想去,唯一成型可靠的渠道就是流量的变现。于是,Google Adsense 和百度联盟便成了我最初尝试的盈利模式。技术上的实现是在界面的显著位置嵌入一个 Trident 视图,在程序启动时访问一个指定的页面,这个页面上嵌入了 Google 或百度广告。但是,这个模式存在以下几个问题: Google Adsense 的审查是很严的,在我嵌入广告页面后没几天就受到了帐号被禁的待遇。 最重要的是,虽然嵌入了广告页,但是没有几个用户会主动点击广告的。 于是,我作弊了。我采用的方法极为隐秘:当用户在分析和谐漫画的时候,软件会提示用户点击广告才能继续下载。这个方法立竿见影,我的收入快速增长,从最初的每月几元提升到了每月 100 元,甚至最后提高到了 1000+ 的水平。当然,百度似乎发觉了我收入的不正常增长,便禁用了我的帐号。 那么,我再为广告联盟的模式补充一条问题(甚至可以说是隐患): 最终解释权归联盟方所有,他们可以随时封禁你的帐号——即使你并没有作弊。虽然我的被封禁并不光彩,但你大可不惮以最坏的恶意来推测强势的一方。 塞翁失马焉知非福,百度联盟帐号的被封禁从客观上逼着我不得不寻找新的盈利模式。于是,我开始尝试一个很常见的方式——捐助。虽然我并不看好这个模式,不过仍然做出了这个捐助的页面。现在回忆起来,这个捐助列表从 0 到 1 的过程是最漫长的一段时期。但自从有了第一个之后,第二个、第三个甚至更多人就慢慢都来了。这个场景让我想起了路边叫卖的小贩:空摊子很难吸引顾客,但如果雇一群托儿来围成个大圈子,那么很快看热闹的人就会围得密不透风。现在看看,如果当时我虚构几个名字在捐助页面上面,也许很容易就可以吸引其他的捐助者。——当然,前提是这个软件自身要让人喜欢,毕竟没人愿意在一坨垃圾上花钱为之鼓励。 不过,与先前每月 1000+ 的收入相比,捐助的收入实在微不足道,而且很不稳定。于是,我开始构想新的盈利模式: 开发支持多语言的国际版本,并尝试注册机制。如果你是个关注 ehentai 之类站点的宅男,那么你就可能需要支付一笔小小的费用来满足自己的欲望。 开发 iPad 设备上的同名软件,用于阅览漫画。App Store 的零售模式较之扑通的软件零售更为靠谱,国人也更愿意为之付费。 这两个设想就作为 2012 年的计划之一吧。请期待漫画控的 2.0 版本。

Categories: 技术的角落 | 3 Comments

伤不起的多音字

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

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

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

在通常的情况下,为了获取一个快捷方式 (.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 的第一个现身处。 查看其调用堆栈,如下所示。 从堆栈中,可以得到两个有用的信息: WINWORD.EXE 真实路径的获取与 CShellLink::_Resolve 有关。而 IShellLink 之中,也存在一个名为 Resolve 的接口。 WINWORD.EXE 真实路径的获取借助了 msi.dll (Microsoft Windows Installer) 的部分函数,而 IShellLink::Resolve 方法的 fFlags 参数恰有一个标志名为 SLR_INVOKE_MSI。 好了,现在已经猜出了那句最重要的代码,可以堆码一试了。 1 2 3 4 5 6 7 … Continue reading »

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

封装

其实我一直觉得封装(encapsulation)是一个有些装逼成分在里面的词汇,因为我总觉得这东西带了些软件工程的味道,而又因为我很讨厌软件工程——大抵是由于我在大学时代接触的软件工程课程太过豆腐渣——总之,我不喜欢“封装”这个词汇,但又十分乐意做封装这件事,因为代码的重用实在太有诱惑力了。 最初自学 C++ 的时候,我对封装的认识就是把数据及其操作用 class 关键字所提供的语法支持包在一起,也就是: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class A { public: A(int a) : m_a(a) { /* Nothing */ } public: int Get(void) { return m_a; } int Set(int a) { int olda = m_a; m_a = a; return olda; } … Continue reading »

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