第一个任务WindowsSDK的界面改写
1. 最开始的思路
韩方最开始发来SDK,最初的想法是因为自己比较熟悉JAVA语言,所以打算采用JAVA调用发来的SDK并写一个接口,进行虹膜图像的录入与匹配。当时的想法是JAVA作为跨平台的语言,会存在相应的接口,应该能实现不同语言之间的库的调用。事实应证了是可以实现跨平台语言的调用。但是C/C++与JAVA之间存在的问题在于参数类型。
2. JAVA改写页面遇到的问题
刚开始时,由于我认为用C++会比较难,所以一心一意使用JAVA来实现用户页面。开始进行技术选择,有两个JAVA GUI的框架可选:Swing框架、AWT框架。由于AWT框架较为古老,所以我选择了Swing框架,对于Swing框架还是上手比较容易,创建JFrame 框架,并在JFrame中添加Jpanel面板。在Jpanel面板中添加图片,该图片选择的是卢岗同事发来的GUI页面底图,底图需要设置参数jf.getLayeredPane().add(background, new Integer(Integer.MIN_VALUE));设置其为最底层,还需要在底图上方加入图标以及文字。文字由于没有卢岗同事原UI界面的那个字体,故我采用了“微软雅黑”代替。同时文字还能实现加粗等功能,直接调用GUI的库的函数就能实现,较为简单。
到中期,应该实现按钮的添加,按钮功能添加了“事件监听器”,添加鼠标点击事件作为一种监听的方式,鼠标按下事件发生,则创建一个新的对话框,CheckMonitor.showFileOpenDialog(jf,false);[上方CheckMonitor类为我自己创建的类]对话框为文件选择器。同时也要设置话框的风格,我设置为跟随系统风格 String lookAndFeel = UIManager.getSystemLookAndFeelClassName();这时页面看着会舒服很多,之后定义文件选择和限制选择文件类型、单选或者多选。这部分还是比较容易的。
到后期时,首先要未雨绸缪,点击事件选择的文本要有一个参数来接收,作为选择虹膜BMP图像需要获取选择的图像的路径,但是我忽略了方法中少写了个s,导致我以为那个类中根本没有那个方法。。。jfc.getSelectedFile();这个方法和jfc.getSelectedFiles();花费了我很多时间。好在最后完成了该部分。
真正有趣的部分就在于调用DLL库函数的问题!!!最开始百度方案选型,看到了可以通过JNI框架进行实现,当时我通过B站看到如果要这么办,就必须改写C++的头文件,此时韩方发来的文件中我只关注了bin文件夹下的可执行文件,和dll文件夹下的dll库文件,及doc文件夹下的开发文档。因为Sample实例文件下我认为C++我是看不懂的。此时我看到JNI可能可以实现所想要的功能,但是苦于不了解C++,所以我又在百度上查找有没有代替方案。最后找到了个JNA框架,JNA框架比JNI更人性化,它不需要改写C/C++头文件,接着我就裂开了,,,到目前位置GUI页面编写时间用时不到5天(我记得好像是3-4天完成的),然后开始漫长的DLL库调用。我记得用了好几周都没完成(好像是三周半)。
JNA遇到的问题:向C++的dll库中的IRIENCE_GetEnrollTemplate();函数传入String类型会报错,传入byte[]类型不报错,但是并不会成功。当时一周目的时候我还不了解C/C++的基本知识与概念,尤其是C的结构体。我把Enroll函数中的const LIVE_IMAGE /*IN*/*pImage形参当成String类型,我甚至不知道const是个什么数据类型。。后来跟朋友探讨后看头文件才发现LIVE_IMAGE结构体感觉像是个类。。。。他有好几个变量,而且LIVE_IMAGE结构体中还嵌套着另外一个IMAGE_ROI结构体。。。LIVE_IMAGE的结构体中有一个const unsigned char *这个指针,这就导致我懵逼了。我根据JNA的官方文档了解到unsigned char需要使用String参数来传值。可是用String传参直接会抛出“内存溢出”的异常,程序直接崩掉。但是传byte []数组进去就不会有问题。wdnmd这么离谱!!然后我就裂开了。这时候时间为二周目。我感觉这样下去不行,此时技术总监汉杰总正在出差。
后面有意思的来了,我看了一下Sample文件,发现欸,好像有个新天地。这里是SDK软件页面的工程文件,这里的IrienceSDK_SampleDlg.cpp这个文件看起来挺有意思,然后第三周目开始汉杰总回来,我也上报了问题点,他让我尝试着用C++做,也让我要学C++语言。这时我就开始了啃cpp对话框页面的源代码之路。这时候我依旧懵逼,然后我把IrienceSDK_SampleDlg.cpp和它的头文件看了特别多遍,看到IRIENCE_GetEnrollTemplate();函数时,我裂开了,我发现这 ™ 的要调用bmp_func.cpp里面的函数进行图片处理。然后我依旧不死心,以为发现了新大陆,之后继续用java处理bmp_func.cpp中的loadBMP();函数。int loadBMP(const char *filename, ch_vec &bmp, int &width, int &height){} 这个函数中const char *filename指针JNA官方文档还是说它是传入String类型,我醉了。。传进去依旧抛出“内存溢出”的异常。传入byte[ ]数组类型它又不识别,而且我想尽办法将String类型转化为byte[ ]、或者直接BMP图片转化为byte[ ] 类型都出错。题外话:我将bmp_func.cpp转化为dll库文件了。然后发现了JNA的另外一个好玩的,有个JNAerator.jar的工具可以自动将dll库转化为JAVA的接口。这样感觉能确保手撸接口的参数准确性。此时证明了如果要通过JAVA来调用接口,我感觉需要点时间和技术的沉淀。这时候三周半目已经过去,是时候通过C++来改写了。
图:JAVA改写的界面,
3. C++改写GUI页面
经过JAVA的尝试后,还是通过C++写界面更为合理,因为JAVA中没有指针的概念,而且传usigned char* 型指针到DLL文件中较为困难,在C++工程文件中的.rc文件中可以直接修改图形界面的位置和属性。
在刚开始接手C++的时候,啃了一段时间C++的代码,也了解了一点点C语言的语法之类,在处理MFC框架的页面时候我还是根据卢岗同事发给我的UI底图进行一点点的搭建,过了一段时间感觉为什么不直接用一张完整的图片贴在程序后面呢??后来这么干感觉效果还比较好,而且降低了很多步骤。
上图是为了完成页面背景采用的方法,直接完整的底图。
在MFC改写界面的途中,按钮中要添加图标,那时候我天真地以为这个对于新手来说应该几下就能搞定(虽然最后很简单就能解决,但是在没有方向的时候找到一个合适的方法真的很难)。
最开始按钮的图标在百度上找到的方法是将一整张图片作为按钮图标,设置一个“按键监听器”实现点击切换图片的功能。我幼稚了,对于MFC的图片要求是BMP图片,但是切图下来的图片为png格式,通过PS转换,图片外围会有一层白色的边框(这是BMP固有的特性,BMP图片必须是矩形)。将BMP图片作为按钮图片显得很无力,边框大小和图片不匹配而且很丑。
上图中的代码就是采用BMP图片作为按钮,LoadBitmaps()可以有2两个形参,分别代表按下前的图片样式,和按下后的图片。如下图。
在此时我甚至还想去找将bmp图片完美和按钮图标融为一体的方法,网上说微软为此定义了一个CDC::TransparentBit()函数来解决这类问题,但是粗略观察下,觉得用这个函数肯定很困难,这个函数应该是处理png转化为BMP位图产生的白边的函数。因此使用bmp图片作为按钮图标根据这个项目的方案不可行。
Ps:在放弃bmp做位图后记得将按钮控件的Owner Draw参数改为False,否则会报中断异常的错误。
最终解决办法:百度搜索的时候换了一个思路,之前搜索的是“在按钮中添加图片”,后面改为“在按钮中添加图标”。完美找到解决办法。使用ico图标就能解决此方面的烦恼。而且ico图标最好通过专业的软件转化,如果只是直接改bmp的后缀名,在VS中也是不可用的。
Icon图标可以完美嵌入到按钮上。至此目前工作已经基本完成。
总 结
在改写WindowsSDK时候方案选型很重要,在时间稍微紧迫一点的时候需要及时调整思路,先把成品做出来,无论是否真正领会其精髓。先把结果拿出来给领导老板们看,最后还是要回归本质。目前的C++改写工作只是给软件换了一层皮,真正要了解的是其底层调用的机制,以及图片处理和各类参数之间的关系,比如在int loadBMP(const char *filename, ch_vec &bmp, int &width, int &height){ } 函数中,传进去的虽然是filename文件名,但是处理出来的图片确实一个&bmp的一个地址,这其中的调用处理机制还有待学习。
在完成这部分页面修改后,我还打算继续使用JAVA完成SDK中dll库的调用,以及了解掌握bmp_func.cpp处理图片的方法。只有这部分掌握了才能算勉强入门了吧。