善意提醒

如果您打开本站很慢,布局排版混乱,并且看不到图片,那么可能是因为您还没有掌握用科学的方法上网的本领。

2026-02-04

CEF的异步CreateBrowser造成的问题及解决方案

图片由 Google Gemini 3 Pro + Nano Banana Pro 生成

公司有个软件,用到了 CEF 浏览器。CEFWebClient 是一个 CEFClient 的子类,作为一个子窗口放在另外一个由我们软件控制生命周期的父窗口中的。这个父窗口的创建,有时候是自动的,而且可能会短期内进行多次。因此,我们遇到了一个问题:如果父窗口太快被「干掉」,那么可能会 Crash。

父窗口的这个行为,当然可以被认为是「不正常」。但首先我的问题是「为什么」。为什么会出现这种事情?

一年前,查这个问题费了我不少时间和精力。本文主要是把相关经验给传承下去,因此接下来我就尽量长话短说了。

CEF 中创建浏览器的方法是先 new 一个 CEFWebClient,放在智能指针里面,然后交给 CefBrowserHost::CreateBrowser() 去创建窗口。这一步许多人都知道,教程和文档上都是这样写的。不太为人所知的细节是:CefBrowserHost::CreateBrowser() 可能是异步的。

这里就要介绍一些上下文了。
跟 Demo 不同,我们的软件,把 multi_threaded_message_loop 设置为 true。几乎是必须如此,因为这个程序并不单纯是个浏览器而已,没法把主线程消息循环让给 CEF。
在多线程模式下,CEF 会有自己的一些线程用来干活。包括创建浏览器窗口这种事情,也是在它的线程中,而不是调用线程或主线程。

因此,CefBrowserHost::CreateBrowser() 是在另一个线程中进行的。这一点我一开始确实没想到,建个窗口而已,谁会想到 CEF 要搞成异步的啊?官方文档也没强调这个事实。这导致调用线程在函数调用返回时,浏览器窗口还没真正创建起来,只是把这个任务给安排了下去而已。如果父窗口在浏览器窗口真正创建前被销毁了,实际创建浏览器窗口的时候就会出问题。

我们的调用线程其实就是主线程,一开始尝试了让主线程等着,但不行。不管是用信号同步,还是用个 while 循环暴力堵住,都会导致死锁。很明显,CefBrowserHost::CreateBrowser() 在子线程中先是作了一些准备工作,然后又用到了主线程,毕竟创建窗口只可能在主线程中进行。可能是一个 SendMessage 类似的操作。我们是直接用了 Spotify 预编译好的 CEF 二进制分发包,并没有从 CEF 源代码从头编译,所以没有往里面深究。总之如果不把主线程空出来,后面的事情没法继续。

简单休眠 20ms 再放行貌似就可以解决问题,似乎是因为工作线程那边的准备工作已经完成了,已经能够正确应对父窗口被销毁这种事情了。但 20ms 时间够不够?谁知道呢。肯定不能靠这种方式在生产环境上运作。最好是有个判断标志可以判断出准备工作做完了没有,但目前 CEF 对我们来说是个黑盒,也没留这个窗口,我们只能想办法去探上一探。

CefBrowserHost::CreateBrowser() 的第二个参数,是个 CefRefPtr 智能指针,具备引用计数。这个引用计数,对调试器是可见的。我盯着看了一阵子,发现只要引用计数上升到一定程度再放行,就没有问题。貌似工作线程在「准备阶段」把这个智能指针挂到了主线程够得着的地方了,以便创建窗口时访问。只要这个操作已经完成,此后即使父窗口被析构,引用计数也不会有被降为 0 的担忧,后面就能正常工作下去。

这个引用计数在cef_base.h里面,class CefRefCount的私有成员,

//
// Class that implements atomic reference counting.
///
class CefRefCount {
 public:
  CefRefCount() : ref_count_(0) {}

  ///
  // Increment the reference count.
  ///
  void AddRef() const { base::AtomicRefCountInc(&ref_count_); }

  ///
  // Decrement the reference count. Returns true if the reference count is 0.
  ///
  bool Release() const { return !base::AtomicRefCountDec(&ref_count_); }

  ///
  // Returns true if the reference count is 1.
  ///
  bool HasOneRef() const { return base::AtomicRefCountIsOne(&ref_count_); }

  ///
  // Returns true if the reference count is at least 1.
  ///
  bool HasAtLeastOneRef() const {
    return !base::AtomicRefCountIsZero(&ref_count_);
  }

 private:
  mutable base::AtomicRefCount ref_count_;
  DISALLOW_COPY_AND_ASSIGN(CefRefCount);
};

还好是在头文件里面,而且只是一个权限问题,因此连重新编译 libcef_dll_wrapper.lib 都不需要。魔改了一下,我们就这样用下去了。

至于引用计数要增加到多少才算是可以放行,我们取了一个经验数据。但这个方案我们一直用得有点担心。最后还是遇到了问题。重现出来一查,就是这个经验数据不再适用了。

正确的解决方案,还是要回到 CEF 的官方机制 OnAfterCreated() 上来。
官方是这样说的:一个 CefBrowser 有没有创建成功,一定要等到 OnAfterCreated() 被调用。不管是要干什么正经事情,还是要「去死」,都得等到它调用了以后。
实际试下来,虽然 OnAfterCreated() 的调用比引用计数「到位」要迟,但的确可以保证不出事情。

所以事情其实没有那么复杂:如果 OnAfterCreated() 还没被调用到,那么就不能关闭父窗口。不能关闭怎么办?先把窗口给 SW_HIDE 了,然后向 CEFWebClient 作个标记。等到 OnAfterCreated() 被调用的时候,再向父窗口发个消息让它自己销毁,调 DestroyWindow() 就行。

经验教训是:一切还得按规矩办。自己想土办法,或许能在某些场合解决问题,但终究不是长治久安之策。
但是,虽然没有看或调试源代码,不过这次也算是刺探了一下 CEF 的一些内部运行机制,也是有好处的。

2026-01-30

冷暖自知

大约一年以前,公司楼下,开了一家咖啡店。
装修貌似从夏天就开始了,进度一直比较慢,磨磨蹭蹭,到2024年总算是正式营业了。

不过,说起来是「店」,可能更像是一个柜台。在原本的大堂里面,装修出来了一个吧台的样子,买来咖啡机,找了店员,然后就这样做起了生意来。

说实话,我们从一开始就不太看好。把柜台开在大堂里面,那么客流就比较受限。我们这栋楼又是一栋六层楼的老楼,并没有太多人。大堂也远没有园区里其它更新一些的大楼气派。然而老板招了三个员工,三个小伙子挤在吧台里,一看就知道有危险活不下去。过了一阵子,店员就只剩一个了,老是蹲在大堂门外抽烟,看来生意不太好。

刚开业的时候,推出了一波2元买美式的促销「活动」。同事还是很「给面子」,去薅了一杯,觉得太苦,于是往里面加了可乐。多亏了他这种勇于探索的精神,我们都知道了这样做会得到一杯酸啤酒味的东西,以后应该没有人再去尝试了。

咖啡店老板看来还是个爱折腾的人。过了一阵子,灯箱有变化,说是研发了「牛肉面」。我看到了就很疑惑,这都什么跟什么啊?

然而这老板的确是把一楼的一家倒闭掉的服装店(把服装店开在一栋老旧的写字楼里面,有生意才见鬼了)的位置给盘了一半下来,真的开了一家牛肉面馆。我后来上下楼一直闻到很香的味道,知道有面馆,但一直以为是从楼里面进去。直到最近我才知道它的正门开在旁边,通往停车场。

我会知道,因为我看见楼里面的门(其实应该是后门)上贴着东西。

通知书-页1
通知书-页2

老板半年多欠了10万的店租和水电费。难怪我有好一阵子没见到咖啡吧开业了。
这下咖啡机什么的估计要被卖掉折现了。可惜了柜台里面那一排排的原料,乳品估计都坏了,苏打水应该还能用吧。

这是另一处地方打出的广告

生意肯定是不好,但店租不能少。不过我估计前面一年半年应该是类似这样免租金的。老板可能也想好了,先干一阵子,没起色就跑,不然沉没成本也太高了。但装修钱还是砸里面了啊,说到底还是不明智。

这年头啊,还是得少创业,多苟着吧。

2026-01-26

两盆冷水敬AI

我要给 AI 泼两盆冷水。

图片由 Google Gemini 3 Pro + Nano Banana Pro 生成

第一盆是泼给 Vibe Coding 的。

很多鼓吹这个的文章,都认为它将导致大量的 Software Engineer 失业。更有甚者觉得整个软件行业被会颠覆,因为人人都能写代码。

是不是这样,我目前还不想去讨论,毕竟世界一日千里。然而,跟大家说一个常识:「AI就是世界所有知识的平均数」。

你现在觉得AI比你强,那是因为在你问的那些方面,你的知识水平还没到及格线。AI厉害的地方在于它能低成本地快速补全你欠缺的东西。但是如果你是某个方面的专家,你就会发现AI还是离你有一段距离。在真正的专家那边,这段距离不小。

通过 Vibe Coding 或许能快速获得一个「还能用」的软件。这个软件或许能快速解决用户当前的某个需求。如果这个需求很容易描述的话,这个软件也用起来也挺不错。比如写个「俄罗斯方块」游戏之类。不过一旦需求复杂起来,要描述的难度也就急剧上升,系统内部的复杂度大增,出问题的可能性也随之大增。众所周知,软件开发最麻烦的地方,其实是「八二原则」里面那些 80% 用来解决性能以及鲁棒性问题的代码。如果不在这些地方下足工夫,那这种软件,其实无法「交付」,只能自己用用。

在我看来,AI其实蛮适合解决这类问题的,因为AI「不嫌麻烦」。前提是人也要不嫌麻烦,不厌其烦地提要求,从各种角度假设,让AI检查再检查。如果能有这种意识,已经是一个超出平均水准相当多的「产品」兼「测试」人员了。如果说要被淘汰的 Software Engineer 就是这类既不具备产品意识也缺乏质量意识的人,那我只能说句「活该」。但话说回来,既然是如此的人才,计算机语言真的那么难学,非得绕着走吗?

总之,普通人的不可能三角——高质量、低门槛、易获得——在这里依然起作用。AI 确实缩短了三角形的边长,让平庸的代码产出变得廉价,但它并没有改变三角形的定律:真正的顶级质量,永远不可能通过「一键生成」这种低门槛的方式获得。

第二盆要泼给 AI 写作。

周末看了一些号称完全由AI生成的文章。用到了多个Agent,互相协作,的确蔚为壮观。
然而,看了以后,给我的感觉就是:没有灵魂。

AI讲逻辑是很厉害的,说起话来头头是道。我认为,一些科普、分析、说明性质的文章,换句话说,议论文和应用文,是行家里手。略加提示与输入,文笔也不是好不起来,模仿一些文学大家或许都绰绰有余。

但是,它不能给我新的东西。

我不太知道如何形容这种所谓「新的东西」。AI写的文章,肯定有让我眼前一亮的部分。但是,我想看到的,来自人类自身的体验和感悟,一些鲜活的事例,个人的见闻,没有。如果有,那也是人喂进去的,不算AI自己的东西。

我也知道,AI目前还只能加工,无法去「采风」。这不算它的错,只是它目前的局限之所在。我很期待有一天,它能够有自己的东西,但至少目前还没有。

所以,目前我只能看着这些一篇篇滴水不漏的文章,完全没有阅读的欲望。或许我喜欢的不是这类文章吧,我可能更喜欢随笔一些。「讲道理」没关系,但更要有故事。

我想起了《奇点天空》里面的「节日」,近乎贪婪地索求「故事」,不惜以「丰饶之角」来交换。或许,这就是人类存在的意义吧。

2026-01-15

逻辑的奴隶?这次我把AI领进了死胡同

最近有同事问我:为什么公司旧版软件在执行某个特定操作时,屏幕上的一部分 UI 就不动了?

说实话,这套旧代码现在成了公司里的「考古遗迹」。唯一深谙此道的同事已经财务自由,移民香港了。我对这个细节也并不非常清楚,只能去看代码。相关代码错综复杂,看得我眼花缭乱却毫无头绪。

想到最近 Google Gemini 3 Pro 的强劲表现,我索性把代码全扔给了它,开启 Pro 模式进行深度分析。

为了「提高效率」,我在 Prompt 中加入了自己的判断:「我认为是相关的 Windows 消息被过滤了。」在我看来,这再自然不过——既然界面停下了,那逻辑上一定是忽略了导致绘图的消息。我只是找不到过滤逻辑的标志位而已,而这事正适合让 AI 靠「蛮力」深挖。

从逻辑上讲,AI 显然非常「同意」我的判断。它一头扎进了我给出的代码里面,然后得出了一些似是而非的结论。我核对后发现,有些结论可以直接排除,有些则根本不符合事实。这意味着,顺着我的思路走下去,连 AI 也找不到真正的原因。

对话进入了死胡同。而 AI 在这种情况下似乎不知道「退出来」,只是机械地一次又一次给出「可能是……原因」的无效推测。最后我不得不靠调试来自行解决问题。

真正的原因,是那位同事在软件界面上遮挡了一个不进行重绘的透明窗口。

图片由 Google Gemini 3 Pro + Nano Banana Pro 生成

事后复盘:我在这里犯了一个典型的错误——我不该在一开始就说出我目前为止的判断。

我反思,如果当初少说一点,让它自行研究,结果会不会更好?我们一贯以来对于 AI 的使用方法,基本上都是「让它知道得越多越好」。我甚至曾这样建议别人:『别隐瞒,把你知道的东西事无巨细都告诉它,哪怕你认为没用的也要告诉它。』

这个原则本身没错,但前提是:你告诉它的是「事实」,而不是「错误的观点」。

很多人类尚且无法区分「事实」与「观点」,AI 可能就更不知道了。在我看来,我的提问还有改进的空间:

  1. 明确标注信息属性: 在 Prompt 中清晰指出哪些是明确的事实,哪些是模糊的现状,哪些仅代表个人的观点和推测。
  2. 允许 AI 「退后一步」: 对于 AI 而言,这只是在做题。Prompt 越具体,求解范围就越窄。我们不能只是把 AI 领到迷宫门口,然后让它进行探索,却不允许它「爬墙头」。

人类会犯错,然后学到东西。AI 也会。如果 AI 能「对抗」人类的意志,我不知道这是好还是坏?

总之,共同进步吧。

2026-01-13

Internet好像也没有那么危险嘛

图片由 Google Gemini 3 Pro + Nano Banana Pro 生成

周日晚上,突然接到公司同事的电话,说我手上有一台服务器「被人攻击」,所以机房要把外网IP给黑洞24小时。

挂了电话之后,想想不对,那台服务器并非生产环境,平时都是内部自己用,要被攻击也轮不到它。

这台服务器,主要都是运维同事在用,我自己了解不多。去年年中,运维同事辞职,我更是早已不负责相关业务。这台服务器有点「三不管」的感觉:登记簿上还挂在我名下,最近没什么空去推进蛮复杂的交接工作,所以还没轮到它;实际管理者没什么能力进行管理,主观意愿上也不想去管;它暂时也没什么多大用处,几乎算是「闲置」状态。

然而,我总觉得自己对它还有「责任」,于是走跳板机从内网连上去又看了看。这一看就让我冒冷汗了。上面居然有个squid,而且貌似是「向全世界开放」的状态。让Google Gemini帮忙统计了一下access.log,发现晚上出事前一分钟就产生了3GB的流量,峰值可能确实把机房设定的阈值给顶破了。所以这应该就是直接原因了。

可是,我不记得自己有在这台机器上搭过squid,也不记得运维同事搭过。他是个谨慎的人,应该不至于干出这种事情。那是谁干的?

再仔细看,简直大汗淋漓了。还有一个未能投入工作的OpenVPN,以及一个名叫proxy的账号,gid是0。完了。什么时候被黑的?!

这个proxy账号看起来是2021年创建的。日志已经灭失了一大半。我只能根据仅有的一点线索,去拼凑还原当时的情况。

「黑客」看来轻车熟路,一上来就登录了root账号,随后创建proxy,设置sudo,然后就去安装squid。

但接下来的情况让我迷惑不解。他光是开防火墙3128端口就折腾了半天,安装squid时也一时输错成了apt-get(这台服务器是CentOS)。装完squid设了allowed_ips,但conf中却大手一挥设成allow all就走了,OpenVPN也是安装了一半就放弃了。前面半截太过顺利,后面半截却画风一转,这不对劲。

我们用的密码都是64位随机字符串,SSH端口也开在非标准端口上。如果这台服务器这么容易被攻破,那么我们剩下的服务器也是凶多吉少。剩下的时间,我都在审查和反省所有可能的入侵路径。还是想不明白,当时到底什么情况?最后凌晨两点钟,实在熬不住,洗澡睡觉去了。


第二天到了公司,我按计划去翻邮件,看看有没有可能出事前是弱密码,后来才改成的强密码。结果这一翻,就发现了「真凶」,也算是一场乌龙。

2021年的时候,当时老板找我要了一台服务器的root权限,说是他儿子要用。
他儿子当时参与了我们的一些项目,也算是「实习」吧,后来去英国读书去了。要这台服务器,是说方便参与维护,当时公司里面还真没人来维护那些东西,所以我也就把这台不太重要的服务器给他了。看起来,当天他儿子拿到root密码,接着就动手了。

大学生嘛,做事情没顾那么多,也是可以理解的。谁还不知道自己大一的时候是个什么样子呢?估计他当时折腾完以后用过一阵子,然后就把这事给忘了。现在他早已毕业,无论是在当地工作或回国发展,总之应该也用不上这个东西了。这代理就这样一直开放在Internet上,不知道有没有人发现,估计没有。虽然日志有限,但看起来2025年一整年都没人用过,直到这个周日。大概总算被什么人给扫描到了吧。

这事说起来就是个内部管理问题。这种事情,就算放在现在,我感觉也没法拒绝。老板自己也大概把这事给忘了。只能是吃一堑长一智吧,以后「出借」的东西要加强审计。以及不要认为别人都会好好善后。哪怕他真的会,但有时候也会偷懒啊。

话说回来,貌似Internet好像也没有那么危险嘛?一个完全开放的HTTP代理,3128端口也是知名端口,居然存在了快五年才被人第一次发现。或许现在ProxyHunter已经没人用了,但是我国政府的扫描器不是一直在巡天么?GFW果真对外不对内?所以啊,世界还真的是一个大草台班子吗?

2026-01-08

关于明尼苏达ICE枪击案的个人评论

看了BBC上关于明尼苏达ICE枪击案的视频。不同的媒体释出的版本,各位也可以都去YouTube上搜一下看看。

https://www.bbc.com/news/videos/cx2ypz4zjvxo

首先说一下个人感觉:纯粹就是谋杀。当然,非要说误杀也行,毕竟动机无法揣测。但肯定是「杀」,不是「自卫」。

被枪击的车辆一开始挡在皮卡前面,然后ICE的人走过去要拉车门,已经拉了一两下了。没拉开,显然司机锁了车门。但驾驶位的窗是开着的,我估计双方也是在对骂。从皮卡司机位下车的那个ICE显然更激动,虽然副驾驶位的同事有做手势让他Calm down,但他把手都伸到车窗里了,看最后那一下的动作,感觉要去从内侧开车门。

没人愿意这样被人拉下车,而且此时也没人拿枪指着司机。司机看起来是想要离开,至少是想要摆脱眼前这种局面。如果换成是我的话,被不怀好意的人包围,并且有人开始拉车门,估计也不会愿意呆在原地。

从轮胎的动作看,是一个类似三角调头的操作,往左后方倒车,然后挂D挡往右前方前进。右边的车的驾驶侧车门是开着的,司机为了避让,不可能一开始就把方向盘往右打死,这也是很正常的事情。并且,换档的时候,打方向盘只有一只手,操作速度没有那么快。因此方向盘一开始看似只是回正了,但随后也就朝右了。而且总之在开枪者决定开枪的那个时刻,车辆向右拐,避开他的趋势,已经很明显了。

开枪者一开始并未持枪,并且明显可以躲开。但他的选择是拔枪,然后跟着车辆的移动方向去前凑瞄准,随后在即将失去射击角度的那一刹那开枪射击驾驶员。正常人要是为了保命的话,应该是连滚带爬地向后退,至少上身会是向后闪避的状态,双手伸出试图阻挡车辆,而不是想着去掏枪。

开枪的一瞬间,可以看到开枪人的两条腿的站姿

掏枪干什么呢?在那个时候,即使他打死了司机,一枪爆头,如果车辆真的是正对着他撞过去,在那个距离上,他也无法避免被撞。但司机只是在向右转弯,并没有去撞他的企图。开枪者此时甚至已经错开一个身位了。事实上,开完枪后,他也很简单地就躲开了失去控制的车辆。故此,很难让人相信他开枪的目的是为了「自卫」,特别是作为一个「有经验」的Agent。

我个人感觉他就是想阻止司机离开,本意可能是想击伤司机,没想到一枪毙命。当然也可能是PTSD之下发P疯了。这些都是可以「辩解」的点。但这改变不了事实:他就是想通过开枪达到自己的某个目的,而那个目的绝对不是「保护自己的生命安全」。

中国大陆在2007年也发生过「储户被押钞员一枪爆头」的事件,当时我还写过一篇Blog。过了这么多年,事情是在越来越好,还是越变越糟,各位心中自有评断。无论如何,这些都是不应该发生的悲剧。牺牲究竟能不能换来一点什么,从前的我,希望有,现在的我,但愿有吧。

2025-12-24

订阅了Google AI Pro

看过一些朋友的推荐,也跟Google Gemini一番探讨之后,今天终于下决心订阅了Google AI Pro。

可以有更多的Google Gemini配额了,包括「思考」和「Pro」,后者貌似就最近两天才加上的。以及,更多的Nano Banana / Nano Banana Pro图片配额。还可以用Veo3.1做视频。免费版的Deep Research配额我曾经用满过,所以还真是有必要成为付费用户。

Google全家桶里面也可以用Gemini了。我记得有印象在Gmail里面看到过Gemini的图标,但今天去找的确找不到了。莫非是当时Google放的鱼饵?

另外,对我而言还有一个挺有吸引力的福利:2TB存储空间。太太的Google Photos空间早就满过了,删了一些视频才降下来。我自己也不敢拍太多照片和视频,就怕哪天空间满了出事情。这问题甚至影响到了我的某些旅游的体验,现在不成问题了。

详细介绍见这里,不过我不知道算不算是最新的:https://support.google.com/gemini/answer/16275805

我是选的「包年」方式,因为感觉一旦用上了应该就「回不去了」。
首年$100,之后每年$200。包月正常价是$20,现在也有试用优惠。不知道是不是年底或圣诞节特别搞的活动。反正都要买,不想等几天错过了,就下单了。
我个人是不太在意这些小恩小惠的,何况已经很划算了。

说起来,Google AI Pro应该是普通中国人最容易「够到」的顶级AI品牌的付费服务了。ChatGPT Plus之前对中国用户想尽办法封锁,还砍单封号来着。直到现在也没见得有多方便,甚至连「安全」都不一定算得上。我曾经形容为「被中美混合双打」,为了用个靠谱的AI也真是难为了。

图片由Google Gemini 3 Pro + Nano Banana Pro生成

当然,对于普通人,我一律是劝他们尽量多用AI,哪怕是国产AI。
有在用总比没有在用要强。面对熊,你只需要比同伴跑得快就行。

AI问:那如果是面对狼群呢?

我说:你差不多得了。 

2025-12-22

丢失的Blogger铅笔小图标

图片由Google Gemini阅读本文后生成,真是厉害

之前在自己折腾Blogger,大概就是2023年或2024年把Blogger重新翻出来写的时候,一不小心,把每篇Blog下面的「铅笔」小图标给整没了。

这个小图标是管理员才能看到的。直接一点就可以进到编辑界面。对于我这种时常在回顾自己文章时发现错别字,需要马上去修改的人而言,真的是很方便。把它弄丢了以后,我每次就得根据文章的标题或标签再去Blogger后台找到那篇文章。有时候某些想法只是一瞬间的事情,过了就没了,对于我而言,更可怕的是「过了就忘了」,这也太可怕了!

我一开始还以为是自己之前在编辑主题的时候不小心把它给误删了。但今天想起这事,下决心解决,去看代码的时候,才发现好像不是这么一回事。代码都在里面,但就是不起作用了。

这是原来的代码:

<b:includable id="postQuickEdit" var="post">
  <b:if cond="data:post.editUrl">
    <span expr:class=""item-control " + data:post.adminClass">
      <a expr:href="data:post.editUrl" expr:title="data:top.editPostMsg">
        <img alt="" class="icon-action" height="18" src="https://resources.blogblog.com/img/icon18_edit_allbkg.gif" width="18" />
      </a>
    </span>
  </b:if>
</b:includable>

看到这里我就有点犯嘀咕。cond明显是条件,该不会是post.editUrl没东西吧?
我把cond改成一个必定成立的条件,放到页面上一试,还真是。图标出来了,点击没用处,没能跳到编辑页面。问Google Gemini,它说新一些的主题里面这个图标的确已经被隐藏了。看来Google是铁了心要把这个功能干掉了。

我想自己整,但发现真正的编辑页面的URL好像有两个ID我不知道哪里能拿到。Google Gemini给出的修改方法语焉不详,而且看起来也没有说到点子上。算了还是求助于传统的Google吧。

搜到的第一个贴子是Google自己的页面,Blogger的Support论坛:
https://support.google.com/blogger/thread/242102130/editurl-the-url-of-the-edit-form-for-this-article-deleted

里面有提到这位大神,是一个法语的Blog:
https://bloggercode.orbiona.com/2021/10/faq.html

后面有个英语Blog也提到上面那篇:
https://too-clever-by-half.blogspot.com/2022/08/the-case-of-missing-pencil.html

法语对我而言有不少困难,还好有Google Translate。
我翻到解决方案一节就知道了:看起来那个法语大神给出的就是我想要的东西。
简单地说,把原来的那段代码替换为:

<b:includable id='postQuickEdit' var='post'>
  <!-- /2021/10/faq.html -->
  <b:with value='data:view.isPage ? "blog/page/edit/" : "blog/post/edit/"' var='path'>
    <span expr:class='"item-control " + data:post.adminClass'>
      <a expr:href='data:blog.bloggerUrl path (data:path + data:blog.blogId + "/" + data:post.id)' expr:title='data:top.editPostMsg'>
        <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/>
      </a>
    </span>
  </b:with>
</b:includable>

就可以搞定了。

原因据说是Google慑于第三方Cookies的限制,没法让前台(blogspot.com)和后台(blogger.com)在域名不相同的情况下还能把管理员的登录会话串起来,索性就把整个功能废掉了。

解决方案的原理也很简单:你不让我跳编辑页面,我就自己组URL。知道BlogID,也知道PostID,还有什么做不到的呢?

当然,这里头还有一些诸如「管理员权限的判断」等小东西。按照「八二原则」,真正花时间的往往是在这些事情上。掌握了核心技术,路只走了20%而已。这位法语大神甚至已经把「页面」和「博文」的差别也搞定了。我就不折腾了,直接用她的吧。

照例吐槽一下:搜索结果中,简体中文的页面依然是一无所获,繁体中文有两篇讲到了这个事情,其中一篇涉及到了核心问题,不过最完整的解决方案还是法文以及英文的页面。简体中文在世界文化交流领域里面就是一个小角落,不管卖出了多少玩具电动车,都改变不了这个事实。不要再夜郎自大了。

2025-12-16

如果你还认为自己是普通人

我不认为自己是什么「普通人」。就算我哪怕曾经有过这样的想法,但不久前以及现在也不再这样认为了。

如果你还自认为是个「普通人」,那么,最好赶紧跟下面这种人区分清楚。

你可以说「这个人根本不是什么普通人」,也可以声明自己「并不是普通人」。如果你已经了解此事,但仍然还是默不作声,那么这份恶,你就也有一份。

祝你去世

没有人是「普通人」。你是独一无二的,你就是你。

2025-12-04

这是一个所有人都在推卸责任的时代

某家银行下辖的期货公司发来公文,说我们的软件存在着「DLL劫持」漏洞,限期修正,催得很急。

说起来跟《鬼子来了》一样,翻译官最该枪毙。DLL是什么东西,老爷可能不懂,但是一听到「劫持」二字就坐不住了。劫持?打劫?谁打劫?匪徒?要劫谁?黄四郎的觉都要睡不着了。

是不是漏洞,技术上说,当然可以算是。该不该改?可以改,可以不改。对方拿了个自己生成的TextShaping.dll往我们软件的安装目录下一放,Calc.exe被调起来了,就说有问题。然后我拿去往Steam的安装目录下一放,也有问题。人家9700万月活,四千多万的并发在线人数,似乎并不担心。

Foxmail也一样有问题。而且Foxmail比Steam还大条,它默认推荐的安装目录可不是什么%ProgramFiles%之类,说起来问题更严重。但是它们都没改,或者说,没被要求改。为什么?因为没有「主」。都是用户自己下载,自己安装,自我的决定。出了问题,没有人寻死觅活找别人担责。

其实通达信也一样,别看它貌似防了一手,但它仍然可以被DLL劫持。关系到同行,我就不展开细讲了。Resource DLL各位自行了解一下。

最鸡贼的是招行的网银。以前我就吐槽过它,貌似为了「安全」而无所不用其极。这次我又发现它的一个做法:为了防止DLL劫持,它索性把自己放到Windows的系统目录里面去了。

可以可以,相当可以

这样当然可以防止DLL「劫持」,因为它直接把自己和操作系统的安全性绑一块儿了。相当于搬到元首家里去住下了,要死一起死。

解决方案不是完全没有,但本文本质上是一篇吐槽文,不是技术类文章,因此也不展开细讲。

说到底,这些所有的事情,都是为了推卸责任。期货公司真的是为了客户的「安全」着想吗?让这种软件能在客户那已经千疮百孔的机器上「带毒运行」,真的好吗?还真以为自己是常山赵子龙啊?能在恶意环境中杀个七进七出,片叶不沾身?

它们其实只是为了在客户真出事找它们寻死觅活威胁要跳楼的时候,可以拿出一些文件来,对着来「监察」的「钦差」们说上一句「你看我该想的办法都想了,剩下的就不是我的问题了。」

然而问题解决了吗?解决个锤子!


今天又收到一篇公文,是要向我们「确认」软件是否有采集客户的MAC地址并上报。

听上去是个人信息收集的问题,不过在这个行业中,有强制要求必须要这样做的,所以这个行为是合法合规的。要确认的,是我们不能「漏报」。

然而,这个「确认」其实是在耍滑头。自己不想去通过测试来确认,恐怕也无力去确认,于是发文件让供应商签字画押。本质仍然是找人担责。

我本来写了一大段,后来又默默地删掉了。对老爷能说些什么呢?

我觉得「MAC地址能自己动手改」这应该是一个老爷可能不知道的常识。老爷本就应该是不具备什么常识的,所以我就不去败兴了。我能告诉他什么是拨号上网吗?他去一搜,到处都是PPPoE,回过头来跟你说Modem也有连接的网卡。他晓得个锤子,他连Modem为什么叫「猫」都不晓得。

他要押,你就画给他,画个小乌龟,带尾巴。


太太这些天,在办交强险退保的事情,办得劳神劳力,最后我让她算了,就当给保险公司的老爷老娘们拿去买伟哥了。

说起来是很有理有据的事情。没起保的交强险可以退,相关的说法网上一搜一大把,都是支持的。AI分析也没有任何问题,我甚至让Google Gemini和ChatGPT都分别做了Deep Research,法律证据也都很充分。甚至太平洋保险自己都有案例解读,监管层2023年也有相关的Q&A。然而就是办不下来,四处碰壁。

投诉到太平洋客服没什么用,开口就是公司规定。去银监会投诉,只是把事情给你转回去。其实又是踢皮球到太平洋保险,说法还是同一套,总之就是不给退。

在网上刷到一位有相同经历的程序员。看来保险行业内部,特别是客服岗位,恐怕应该是学习过这种案例了。如果松了口承认了相关法规,就得受罚,而且公司层面说是「补偿」而不是正常的办理流程,也就是「口子不能开」,开了就是客服个人的责任。

监管层说是可以退,然后又让个人去跟保险公司「协商」。这情景活脱脱就像我当年去电脑城找12315投诉商家欺骗消费者,然后老爷过来指导销售「你快点跟消费者解释你们都有些什么额外的成本」。我在猜,大概体系内部其实是不允许退的,对外说可以退,对内严惩相关人员。医保不也一样?


其实吧,我就把话说重一点,也说得大一点。从三年疫情就可以看出来,共产党和中国政府其实根本不是在管理,他们也没那个能力。他们就是「责任驱动」,你也可以说他们是被「压力驱动」的。他们所做的一切,都是为了「卸责」,逃避危险。所有事情的动机,都可以用「给自己少些麻烦」来理解。

尽管有的时候,他们貌似反而制造了更多的麻烦,但你如果试图站在他们的立场去想想,就会发现,那是因为他们觉得如果不这样做,就会有「存亡危机」。「人无远虑,必有近忧」,话是没错,然而鼠目毕竟只有寸光,一切终归是偷来的。

看穿了这一层,很多事情,你就可以理解了。虽然逃避责任最好的办法是不担责,不在其位,自然就没责任了。但是有一个词叫做「秋后算账」,他们是最怕的。当年他们这样干过,所以自己最清楚。后面上台的人也不想为现在的烂摊子买单,那么必然有人要担责。猜猜会是谁呢?因此,为了不被秋后算账,现在必须抓牢权力,而且永远也不能松手。非不愿也,实不能也。

上行,所以下效。何况人性本来就是趋利避害的。现在没有利可以趋了,那害总是要避一避的。故而,造就了这么一个所有人都在推卸责任的时代。

即使在公司里,也能嗅到这种氛围。上了点年纪的同事,「混」得最好的,就是那种浑身是刺,搞得别人不想理他的人。其他人也在眼馋他的「清闲无事」,纷纷向他学习。做事情都是趋于保守,少做少错,不做不错。长此以往,一个民营企业,会有什么下场?我不知道,但连老板想的都是「先把这几年熬过去」,以及「找死不如等死」。

如果参透了这些,或许能在眼下处世更加自如一些。我虽然已经看破,但无意为伍。且也先「保守」一下,把自己的心田防守扎实一些吧。


洋洋洒洒写了这么多,其实一句话「社会逐渐趋于保守」就可以概括。这应该也是大家都基本能认同的事情。只不过,最近遇到的想吐槽的事情,一下来了好几件,还是想拿出来说一说,所以絮絮叨叨写了这么多。

以及,有些话,如鲠在喉,不吐不快。真的说出来了,心里竟然舒服了一些,可见还是有毒。