Fan 的个人资料missdeer 之编出来的幸福照片日志列表更多 工具 帮助

missdeer 之编出来的幸福

倚楼听风雨,淡看江湖路——未来的不可知,是我们前进的原动力!花开,然后花落,星光闪耀,不知何时熄灭。这个地球,太阳,银河系,甚至整个宇宙也总会有消失的时候,人的生命和那些相比只不过是一瞬间吧,在那一瞬间中,人诞生,微笑,哭泣,战斗,伤害,喜悦,悲伤,憎恨谁,喜欢谁,所有的一切都是刹那间的邂逅,谁都不能逃脱死亡的长眠……

Yang Fan

职业
地点
兴趣
我跟小妞说,我打3份工,小妞说,不是说180份工吗,我说那是彭彭啊,小妞说,那你以后的女朋友就没有思思那么漂亮了哦~

Windows Media Player

流量统计网
第 1 张,共 10 张

找到关键跳,再强的加密算法也没用

  突然想研究一下软件注册机制中使用比较强的加密算法,能带来什么效果。说到比较强的加密算法,用非对称的算法应该是公认的比较好的选择。流行的大概有RSAElGamalECC了。翻出几本买了好久,都没仔细看过的书,最后发现我的数学基础实在很差,这方面的思维分析能力也很差,最终大致能看懂原理的只有RSA算法。RSA算法的强度建立在大素数因子分解的基础上,只要选择足够大的两个大素数,当然还要受制到当前主流硬件运算能力,就能保证一定意义上的安全。
  目前有很多开源的加密算法库和大数运算库,可以提供RSA算法的实现,比如MIRACLCrypto++LibTomCrypt等等。结果发现MIRACL只能免费用于非商业用途,而LibTomCrypt的接口让我比较迷惑而不会用,剩下的Crypto++倒是可以正常使用,接口设计得也非常方便易用,缺点是体积大了点,而且很早以前在网上看到过有人说它有些算法可能涉及到一些版权、专利的问题。
  最后是使用RSA实现软件注册算法的问题,从书上看到,注册机使用RSA私钥对用户名进行解密,解密的结果则作为注册码,用户拿到该注册码,输入到软件注册界面后,软件使用RSA公钥对注册码加密,能得到用户名?不过我发现,在用Crypto++进行计算时发现,随便取的一个字符串作为密文用私钥根本解密不了啊,会抛异常说无效的密文,其次,使用公钥进行加密时,也有限制,对明文的长度有限制,根本不能对很长的,可能是密文的字符串进行加密呀!倒是用 RSA进行签名是可以的,这种签名方式就与MD5、SHA之类的单向散列算法效果类似了,只能确认明文是否被修改过。
  不过我到PEDIY的论坛上问了问,其实采用多强的加密算法没多少区别,只要破解者能找到最后判断的语句,找到关键的那条跳转语句,就可以爆破了,前面的那些复杂的加密解密运算全做无用功了。汗!

修正3处内存泄漏

  不知从什么时候起,程序退出时,VC的输出窗口中就会打印一大片内存泄漏信息。开始还没怎么在意,认为只要程序功能正常,有点儿内存泄漏实在不是什么大不了的事情。最近随着代码的增长,似乎打印出来的内存泄漏数量也随着增长了,我仍然有些想逃避,安慰自己说不定是wxWidgets的问题,其实用膝盖想都知道,这种可能性太小啦!
  晚上实在忍不住,或许真是只是想证明,确实是wxWidgets的问题吧,决定看一下到底是哪里泄漏的。因为程序使用嵌入Lua,很多功能都通过脚本扩展实现了,C++的代码量不多,基本上只剩下一些主框架界面的创建和消息响应转发的工作,所以没花多少时间,通过分段屏蔽代码来检测内存泄漏的源头。
  最终发现有3处,而且确实都是我自己的代码有问题。
  首先是有个singleton在程序退出前没有销毁掉,好像在GoF中还是《Modern C++ Design》中说的,这种情况的singleton销不销毁影响不大,不过用Loki中的Singleton是不会有这种问题的,所以还是为了美观起见,主动销毁吧。
  然后,稍稍花了点时间,发现有一处new了一个对象后把指针插入到std::map中,却发现new出的对象个数最后比map中的元素个数多几个,那么最后通过迭代map销毁这些对象时,就有几个漏掉没销毁掉了。我一时间还没想明白怎么会出现这种问题的,后来想起来,有几个对象插入时估计是使用了相同的key,于是只能在map中留下一个。程序运行表现倒是没错,这是一种奇怪的现象,但在这里是合乎逻辑的,但仍然要改掉。
  最后,发现是new了wxFlatNotebook后,就有泄漏。照理说,这种有父窗口的子窗口对象创建后,wxWidgets是会负责销毁的。所以估计是wxFlatNotebook有什么特殊的要求,从sourceforge上找到它的页面,有SVN下载选项,最近一次更新是2008年的事了,估计是不会更新了。它的作者居然就是CodeLite的作者,但我发现似乎CodeLite本身就没用这个组件,作者又自己实现了一套标签系统,不过都放在 CodeLite里没独立出来。对比了一下我使用的wxFlatNotebook的版本,应该是2.1版,跟CodeLite代码里放的那份是一样的,SVN trunk中的至少是2.2以后版本了。于是下载下来,还有sample,这才是最重要的,看了一下sample在主窗体的析构函数中调用了一下 wxFNBRendererST::Free()。把这条语句加到我的程序中,真的没有泄漏了!
  现在舒服了,不报内存泄漏了,能这么顺利地解决3处内存泄漏问题,跟程序架构有很大关系啊!

库使用注意

  上午整了一两个小时,在wxWidgets程序中使用第三方库wxPropGrid,结果发现在VC2008中链接时有几个warning,虽然看起来刺眼,但似乎是可以正常运行的,也没有很在意。然后用MinGW编译链接,最后链接不通过,报未定义的符号,而这些符号是之前用VC2008时报 warning的那几个,这就说明不是库编译得有问题,就是本身程序编译得有问题。
  我先把焦点放在库上,wxPropGrid是编译成静态库的,这不但编译链接选项不同,连有个宏定义(静态库是 WXMAKINGLIB_PROPGRID,动态库是WXMAKINGDLL_PROPGRID)都不同。我仔细观察了该宏定义对源代码的影响,并参考了 wxScintilla的做法,发现区别很小,基本可以忽略。于是我琢磨着如果实在不行了,把wxPropGrid编译成动态库试试。正在这么打算的时候,突然想起来,这个宏定义在主程序中没定义啊!一定是这个原因!于是修改了主程序的配置,加上了这个宏定义,重新编译,发现果然有效,VC中也不报 warning了,MinGW中也可以链接通过了!
  其实这是个老问题了,只是平时很少遇到这种情形,一时没想起来。

渐渐习惯用Lua写代码了

  今天本来打算把那几个菜单项的功能完成的,不过后来又转去做其他事情了,计划执行力不强啊,唉!
  要在Lua中操作xml,在现在的软件开发中,这是个很常见的需求,xml已经变得无处不在了。在神作PIL中提到的是用expat库,呃,我只会用 DOM,所以只好找其他的库了。前些天在luaforge上看到一个叫ltxml的,才0.2版本之后就没更新过了,用的是TinyXPath和 TinyXML,我决定好好考察一下。
  它没有文档,只有一个readme后面四五行C代码示例,不过这足够了,看一下那个cpp文件中注册的方法,基本可以猜出用法。总的说来,先 require,再用xml.open方法打开xml文件,然后就可以用TiXMLDocument的select等方法得到TiXMLNode,就跟我熟悉的用C++操作DOM的做法一样了。
  说起来TinyXML的表现不差,基本能满足我当前的需要,我有点儿后悔当年花了那么大力气将Xerces C用VC2008编译了一遍,又绞尽脑汁用MinGW编译了一遍,还自己封装了一把,以适应STL中的算法。对于我来说,它太庞大了,让我畏惧,那么大一个却仍然要让Xalan来处理XSLT和XPath。同样,libxml和libxslt给我的印象也差不多,它们甚至没能让我顺利编译!
  昨天说到的,现在程序崩溃总是无声无息地自动退出了。今天想了想,其实之前好像也想到过,会不会是因为LuaJIT的缘故,于是换了官方的Lua的 dll来用,果然在原本会引起退出的地方,Lua只是而压了条错误信息到栈中,LuaJIT的行为没跟Lua一致啊!在Lua list上发了个邮件问问,结果一个老表说他没能重现,问我有没有证据,我汗,用Wink录了个8MB的操作录像,然后发现这maillist限制最大附件是40KB,严重超标,还要等人审查。总结一下崩溃的条件,在Lua代码中调用第三方C/C++代码注册的类和方法,如果方法、成员不存在,就会退出。而Lua是能处理成将其识别为一个nil,然后报不能在nil上进行函数调用之类的话。
  总之,一切在向好的方向发展,甚至自己已经能渐渐地习惯于写Lua代码了,不像之前那样非C/C++不爽!

修正问题,展望未来

  经过定位,方法很简单,在有怀疑的地方加入跟踪语句,打印栈大小,发现确实还是在那几个地方,栈中项的数量稳步增加,这才想到,会不会是从表中获取某个元素后,那个表还在栈中呢?看了下文档,也没有相关的说明,只好先作这个假设。在调用表中的函数后,应该弹出两个值,这样修改后,果然过了一个多小时也没退出,而且看栈中的项也确实没泄漏的。
  昨天修正了对TeX代码高亮的问题。原本发现有TEX和LATEX两种lexer,如果直接设置好lexer的话,LATEX只能着色,没有代码折叠,而TEX有代码折叠,不能着色。看了代码似乎也都不用设置什么关键字字符串的,没办法只好到scintilla的maillist上问一下,Neil 回复说SciTE中用的是TEX。于是我又看了一下TEX的properties文件,发现还有4个专用的property要设置,加上后果然好了。开源就是开源,只有通用的scintilla接口文档,却没有针对每个lexer的文档,唉!
  昨天还写了个小程序,用于将原本给另外一个程序用的Scintilla的所有lexer的语言配置文件从xml格式转换成现在正在进行的这个程序可用的 lua脚本。这是个很省事的活,不过这小程序也是花了不少时间修改,因为要生成的lua脚本也在不停地修改。从这看出,xml真是一种存储数据的好格式啊,可以方便地转换为其他格式。要是SciTE的配置文件也是xml的就好咯,想当初为了把众多lexer的配置从properties格式转换成 xml,可是花了我好几个晚上的业余时间的。
  接下来应该要实现其他一些基本的功能,以及好好考虑下如何实现针对不同lexer的各种功能。

狂烈地崩溃

  昨天偶然发现一个超级严重的问题,程序运行一小会儿就会自动退出,什么提示都没有。至于没提示,这已经有一段时间了,照理说,内部状态、逻辑不正常么,可以给个Windows的崩溃报告嘛,可是它偏偏没有,弄得我要跟着崩溃了。
  后来在代码中加入一些跟踪语句,发现出错的原因跟我的猜测一致,内嵌的Lua解释器栈溢出了。这是个很头痛的问题,以前听人说过,如果没有 sandbox,插件运行环境是不可靠的,呃,最出名的是chrome的架构,经典的sandbox。但是我这个程序跟它的情况有点不同,在主窗口和子窗口上都有大量的用户交互操作,以及主窗口和子窗口之间大量的交互,子进程间的通信会很复杂。而且现在引起崩溃的,都是主窗口中的逻辑,所以还是会导致整个程序的不可用。
  昨天晚上调试了好久,发现只要更新工具栏按钮或主菜单项的界面状态的响应函数打开后,过一会儿就会退出。所以最后可以把范围缩小在C++调用Lua函数的那一块代码上。我不怀疑Lua的代码有问题,凭我现在对Lua的了解,即使真有问题,估计我也是束手无策的。既然是栈溢出,而且时间不长就可以重现。我仔细地看了那块代码,又看了Lua manual和PIL,以及Luabind Documentation,发现我一直忽略的一个问题,在调用Lua的C API出错后,Lua经常会把出错信息压入栈中,而Luabind可能会直接将其封装为luabind::error类型的异常抛出,然后我就只是看一下那个字符串内容,却没其他处理了。这是一处错误,应该在提取字符串后,将其弹出。另一处错误是,我这里调用Lua中的函数,都是存放在一个表中的,所以中间无论哪个步骤出错,都应该把先前压入栈中的东西弹出。还有一处错误是,最后我从Lua栈中获取到函数后,用luabind::object封装了一把,然后luabind::call_function来调用,这时我又直接返回了,却没把这个放在栈中的函数弹出。
  昨晚解决了这三个问题后,还以为所有问题都已经修正了。今天又测试了一遍,发现过了约半个小时后,程序还是自动退出了,而且连那exe文件都没了!我要疯了!
  唉,这什么都是从零开始的,风险实在太大了。使用wxWidgets是第一次,复杂的内嵌Lua扩展框架是第一次,使用Luabind是第一次,使用 wxLua是第一次,把所有东西混在一起用更是第一次!而且很不爽的是,已经用惯了MS的解决方案的我,没有像MSDN这样的大而全的文档极不适应,那些说使用开源的东西成本低的人,不知是真的短视,还是别有用心呢。

使用SWIG和Luabind的问题续

  这几天似乎渐渐进入了正轨,发现并解决了一堆的问题。
  从github上下载的0.9版Luabind可能不稳定,毕竟是没有正式发布,之前没有经过仔细的试用,后来实际用的时候总是这也不行那也不行,万般无奈之下退回到正式发布的0.8.1版。两个版本确实有不少区别,至少源文件个数都不一样。在实际使用的过程中发现,0.8.1中如果注册一个类时,无论有没有注册它的构造函数和析构函数,都要求它们是public可见的,而0.9似乎没这个限制。
  比较郁闷的是,没找到一个可靠的办法,把一个类中的STL的容器类型的成员变量传给Lua。Luabind中有个 return_stl_iterator策略,似乎是可以把它传给Lua,也能在Lua进行迭代,但是在应用程序退出时,就会报未处理异常。我想以后如果真的一定有传容器的需求,可以试一下SWIG。
  在Lua和C++之间传递字符串是个很头痛的问题。C++中的字符串类型太多了,几乎每种框架或大规模类库,都会有至少一种自己的字符串实现类。在 wxWidgets中用的是wxString,但跟Lua交互的最好就是C的字符数组。如果是只读的访问,那还是比较容易处理的,Luabind默认处理了std::string类型的转换,SWIG中只要包含了std_string.i,也是差不多透明地处理了。但是如果要把字符串作为输出参数,那就头痛了,Luabind中是有out_value策略和pure_out_value策略,但实际上我发现在Lua中用的时候程序就崩溃了。暂时也不想再深究这个问题了,顶多在必要的时候考虑修改一下接口了。
  又是才发现,SWIG和Luabind中注册的类型是不能互相混用的。比如SWIG中注册的某个函数,返回一个类的实例,而Luabind中又注册了这个类的话,是不能作为那个返回值的类型的。所以有时候可能需要在两边都注册一遍某个类型。比较安慰的是,基本数据类型还是能混用的,呵呵。
  对于有缺省值参数的函数,SWIG会自动封装成多个函数,每个函数使用相同的名字,只是参数列表不同。而Luabind一次只能注册一个函数,那个完整参数列表的函数,如果想要达到SWIG那样的效果,大概只能自己老老实实一个一个注册上。
  再有是Luabind中,不能随便注册char*类型参数的函数,要注册得指定out_value策略,不过说到这里我就有点疑惑,Luabind好像不支持多个策略的啊,如果有多个参数要指定策略怎么办?而且虽然我没经过测试,但我想除了char *外,其他的自定义类型作为参数的,都有这个问题吧!

混用SWIG和Luabind的问题

  花了两三天时间,总算是从头到尾看了一遍Luabind的使用手册,呃,为了确保没有漏过的内容,甚至还把整个文档都翻译了一遍。在看文档的过程中,还是比较感叹该库做得功能强大的,不过在实际用的时候就比较郁闷了。
  我用Luabind绑定了一些类和函数等内容到一个模块中,用了SWIG把几个头文件处理了一遍的,把所有内容都放到同一个模块中。一开始发现在Luabind中绑定的类在Lua中死活不能用,调用成员函数时,总是说那个类是个nil,不能被index。
  后来尝试了下,把Luabind中绑定的内容放在全局域中,发现居然可以了!虽然这样似乎可以工作,但我想让所有由内部C++代码提供的服务放在同一个模块中。后来想了想,把SWIG和Luabind的注册顺序换一下,改成先注册SWIG的内容,再注册Luabind的内容,嘿嘿,真的也可以了!我猜想可能Luabind的注册内容是采用追加的方式,而SWIG却是覆盖方式。这个猜想没有经过证实,只是如果真是因为这个原因的话,也是可以理解的。

从C++传递窗口给wxLua

  描述一下问题,程序主框架是用C++实现的,GUI框架用的wxWidgets,有一部分功能通过嵌入Lua解释器调用Lua脚本完成,如果要在 Lua脚本中用wxLua实现个对话框的这种情况下,需要一个父窗口,而最好的父窗口是由C++实现的,现在就需要能把C++中实现的窗口传递给Lua,并让wxLua作为父窗口使用。
  问题根源是出于偷懒的考虑,我把C++中需要能被Lua调用的类、方法等用SWIG嚼了一遍,生成了脱水代码。这样用SWIG返回的wxWindow*跟wxLua中的wx.wxWindow就不是一种东西。
  解决方案比较quick and dirty,过程略有点曲折。昨天偶然在wxWiki上看到一段文字,描述了如何将MFC的窗口关联到wxWidgets中,我就想如果wxLua是严格移植了wxWidgets的话,应该也很容易实现的。不过很沮丧的是 SetHWND、AdoptAttributesFromHWND、Reparent这三个方法wxLua一个都没有实现!于是又去wxWidgets的 Google group上找了找,发现一个叫AssociateHandle的方法,可以关联一个原始的Windows窗口句柄。到了这一步,我已经没有其他出路,只好修改wxLua的源代码,在wxLua\modules\wxbind\src\wxcore_windows.cpp这个文件中给 wx.wxWindow添加一个新的方法void SetWindowHandle(long hwnd),代码实现可以抄SetWindowStyle的,函数签名相同,里面的步骤也类似。最后要在wxWindow_methods的初始化列表中添加这个新增的方法,就可以重新编译wxLua了。
  有了这个修改过的wxLua,再在自己的应用程序中暴露一个方法以便Lua获取主窗口的句柄,接口就可以在Lua中这样使用了:
  local win = wx.wxWindow()
  local hwnd = frame:GetMainFrameHandle()
  win:SetWindowHandle(hwnd)
  这个win就可以作为wxLua中创建的子窗口、对话框的父窗口了!

划分脚本插件架构的职责面临的问题

  今天整理了一下项目中使用的第三方框架、库的列表,很多,有C++的,有Lua的,突然面临一个迫切需要解决的问题:怎么决定某个功能应该由C++实现还是由Lua实现?
  最早决定让项目成为一个由C++构建主体框架,由Lua脚本扩展实现其他的业务逻辑时,只是单纯得想让C++完成一部分最核心的功能。但现在的问题是,怎么判定一个功能是否够核心,以及即使够核心了,还得考虑其他一些因素,包括实现难度,安全保护,代码架构合理性,代码和逻辑共享等。
  众所周知,用不同的语言实现相同的功能,难度和工作量可能差别很大。以前听同事和领导不止一次说起过,一行脚本顶得上一百行C++代码。这也许有点夸张的成分,但正好说明差异巨大这个事实。引起这种差异的主要原因就我自身角度出发来看,在于对语言的掌握程度,不同的语言风格就对不同开发任务的适应度,以及可利用的现成的库和代码的丰富度和成熟度。
  说起安全保护,是今天看到Lua的maillist上讨论LuaJIT时Mike Pall说起源代码保护时才提醒了我。一般说来,用C++编写的代码,经过编译生成二进制代码,比起用脚本语言写的代码生成的字节码(中间码)反编译要困难得多。而我本来考虑让众多功能都通过脚本实现,这就面临一个问题,如何保护自己觉得重要的代码。最早的时候想用Lua的官方编译器编译一把就行了,现在看来这个保护弱得可以。而最近又面临另外一个短期内不可能解决的问题是,我打算嵌入LuaJIT 2.0的解释器,而刚刚才知道LuaJIT 2.0不兼容Lua官方的字节码,只提供源代码级的兼容,似乎LuaJIT也没提供一个自己的编译器,同时即使有这样的编译器,万一某种情况下需要用 Lua官方编译的文件,那么处理就不一致了!Mike Pall的意见是,用个zip之类的东西打包加密就行了,呃,怎么说,确实是个方案,但使得本方案只能自己使用的了,不能让第三方的开发者参与了!所以除非有其他比较完善的解决方案,不然那样的代码只能用C++实现了。
  再说架构合理性,总的说来,到目前为止的进度,自我感觉这样的架构还是比较满意的,当然现在只是一个空壳,只能支持让主菜单、工具栏按钮和右键弹出式菜单的构建和触发都是由脚本插件扩展而成,接着就而对的问题是,像配置选项功能要由谁来做,这种功能有一定的复杂性,又有GUI界面,又有后台处理逻辑,是全部让C++做,还是全部让Lua做,或者是各做一点,那又是各做哪些和各做多少呢?几乎所有同时涉及界面和后台逻辑的功能点,都有这样的问题,究其原因在于,用C++实现了主界面,而在Lua中目前并不能很方便地操作这些界面。原本天真地以为,C++用了wxWidgets做界面,那么Lua中用 wxLua就可以实现无阻碍互通了。现实是残酷的,现在光是想让wxLua中使用C++中创建的主窗口作为父窗口就搞不定!
  最后是代码和逻辑的共享。这个问题不是很严重,如果是纯粹的计算逻辑最容易共享,一些常用的底层功能,两种语言都差不多拥有第三方库来解决。除了 GUI,其他的代码(逻辑)要共享,通过luabind和SWIG可以比较方便地粘合起来。如果粘合起来还是犹豫不决,那就是架构合理性的问题了。
  想不到这次决定构建一个基于C++的脚本扩展框架的应用程序,会引出这么多问题,大大出乎我的意料啊,我是一直以来对风险的估计不足啊!