|
楼主 |
发表于 2013-10-13 10:52:56
|
显示全部楼层
本帖最后由 inkflower 于 2013-10-14 06:02 PM 编辑
二、用perl编写COM组件以及在zmud调用以替代routecreate
好了,现在我们已经用perl实现了寻径和路径简化,下面就到了关键的环节,怎么让zmud和perl程序通信交换数据呢?首先,zmud必须通过某种方法告诉perl我们要从哪个城市到哪个城市,同时,perl应该提供一种方法把经过计算后的最终路径传回到zmud。
怎么让zmud告诉perl程序需要完成什么呢?这时候就要借助COM组件了,关于zmud与COM组件的通信的实现,论坛上已经有玩家发过相关贴子了,这里就不重复了,具体请见:
http://pkuxkx.com/forum/thread-35918-1-2.html
好了,我们知道zmud可以与COM组件通信,但是我们的perl不是一个COM组件呀,怎么把我们的perl代码转化成一个COM组件呢?是时候让perlctrl上场啦!如上一篇所说perlctrl是activelstate公司开发的perl dev kit中的一个开发工具之一,它就是一个转化perl程序为标准的ActiveX控件的实用工具。让我们来看看它是怎么转化的。要记住,它不是编译器,也不是一个perl解释器,它就是一个小工具。
perlctrl可以把符合perlctrl格式的perl源代码转换成windows的DLL。还记得我们在上一篇中的perl源码么,它是单纯的perl代码,你可以让它在perl解释器中运行,得到你想要的结果,比如perl gtpl.pl yz bj, 运行结束后控制台上会打印最终的路径,但我们的目的不是打印,我们的目的是让我们的perl程序接收来自zmud的命令并且把最终计算出的路径传回给zmud。因此,按照我们既定的思路,我们需要先把我们的perl源码写成perlctrl能够识别的格式。让我们一步一步的来揭露转化的过程。
首先,要保证在你的机器上安装了5.8版本以上的activeperl,接下来,安装perl dev kit,在这之后,你就拥有了perlctrl的运行环境,注意,perlctrl是依赖于activeperl解释器的,因为它不是一个解释器。perlctrl生成.dll时将打包一个perl解释器,perl源码和这个源码所依赖的所有perl模块以及依赖的其他.dll,当你运行这个.dll时,这些一起打包的所有模块将协同工作,实际上运行的仍然是perl程序。枯燥的理论终于过去啦,让我来看看我们应该怎么做!
首先,打开一个命令行程序,然后运行perlctrl -t > template.pl,这样保证perlctrl给我们的新.dll生成一个唯一的GUID,这是COM组件的特性决定的,每个COM都必须有一个独一个无二的GUID,如果没有COM知识,那么让我们类比一下,写过mush插件的人都知道要给插件申请一个唯一的ID,所以这里的GUID就是这个唯一的ID了。生成的template.pl会长成这样:- =pod
- =begin PerlCtrl
- %TypeLib = (
- PackageName => 'MyPackage::MyName',
- DocString => 'My very own control',
- HelpFileName => 'MyControl.chm',
- HelpContext => 1,
- TypeLibGUID => '{EFF666F8-8423-474F-81E8-D2ECC92F3148}', # do NOT edit this line
- ControlGUID => '{726CBD22-C14E-4E74-8558-E0C9F0692CF9}', # do NOT edit this line either
- DispInterfaceIID=> '{215A95FA-641A-49D4-9443-F78E691934B0}', # or this one
- ControlName => 'MyObject',
- ControlVer => 1, # increment if new object with same ProgID
- # create new GUIDs as well
- ProgID => 'MyApp.MyObject',
- LCID => 0,
- DefaultMethod => 'MyMethodName1',
- Methods => {
- MyMethodName1 => {
- DocString => "The MyMethodName1 method",
- HelpContext => 101,
- DispID => 0,
- RetType => VT_I4,
- TotalParams => 5,
- NumOptionalParams => 2,
- ParamList =>[ ParamName1 => VT_I4,
- ParamName2 => VT_BSTR,
- ParamName3 => VT_BOOL,
- ParamName4 => VT_I4,
- ParamName5 => VT_UI1 ],
- },
- MyMethodName2 => {
- DocString => "The MyMethodName2 method",
- HelpContext => 102,
- DispID => 1,
- RetType => VT_I4,
- TotalParams => 2,
- NumOptionalParams => 0,
- ParamList =>[ ParamName1 => VT_I4,
- ParamName2 => VT_BSTR ],
- },
- }, # end of 'Methods'
- Properties => {
- MyIntegerProp => {
- DocString => "The MyIntegerProp property",
- HelpContext => 201,
- DispID => 2,
- Type => VT_I4,
- ReadOnly => 0,
- },
- MyStringProp => {
- DocString => "The MyStringProp property",
- HelpContext => 202,
- DispID => 3,
- Type => VT_BSTR,
- ReadOnly => 0,
- },
- Color => {
- DocString => "The Color property",
- HelpContext => 203,
- DispID => 4,
- Type => VT_BSTR,
- ReadOnly => 0,
- },
- MyReadOnlyIntegerProp => {
- DocString => "The MyReadOnlyIntegerProp property",
- HelpContext => 204,
- DispID => 5,
- Type => VT_I4,
- ReadOnly => 1,
- },
- }, # end of 'Properties'
- ); # end of %TypeLib
- =end PerlCtrl
- =cut
复制代码 接下来让我们简化一下template.pl,让它只包含我们需要的部分:- =pod
- =begin PerlCtrl
- %TypeLib = (
- PackageName => 'MyPackage::MyName',
- DocString => 'My very own control',
- HelpFileName => 'MyControl.chm',
- HelpContext => 1,
- TypeLibGUID => '{EFF666F8-8423-474F-81E8-D2ECC92F3148}', # do NOT edit this line
- ControlGUID => '{726CBD22-C14E-4E74-8558-E0C9F0692CF9}', # do NOT edit this line either
- DispInterfaceIID=> '{215A95FA-641A-49D4-9443-F78E691934B0}', # or this one
- ControlName => 'MyObject',
- ControlVer => 1, # increment if new object with same ProgID
- # create new GUIDs as well
- ProgID => 'MyApp.MyObject',
- LCID => 0,
- DefaultMethod => 'MyMethodName1',
- Methods => {
- MyMethodName1 => {
- DocString => "The MyMethodName1 method",
- HelpContext => 101,
- DispID => 0,
- RetType => VT_I4,
- TotalParams => 5,
- NumOptionalParams => 2,
- ParamList =>[ ParamName1 => VT_I4,
- ParamName2 => VT_BSTR,
- ParamName3 => VT_BOOL,
- ParamName4 => VT_I4,
- ParamName5 => VT_UI1 ],
- },
- }, # end of 'Methods'
- Properties => {
- }, # end of 'Properties'
- ); # end of %TypeLib
- =end PerlCtrl
- =cut
复制代码 template.pl里包含了什么呢?它其实是提供了供perlctrl生成.dll的必要信息,他会告诉perlctrl我的progID是什么,我的模块名字是什么,我向外界暴露了哪些方法,这些方法的传入参数和传出参数是什么类型,我向外界暴露了哪些属性,等等。
接下来,我们要把gtpl.pl和template.pl合并起来,这样,perlctrl在执行的时候才知道究竟.dll里面要装些什么内容。第一步,我们把gtpl.pl的内容完全复制到template.pl的最前面部分,让我们把这个新的代码叫做gtplcom.pl。接下来,我们就需要提到perl的package概念。package修饰字表示这是一个perl的模块,如果你对perl有稍许了解的话,也许你曾经听说过.pm文件,这是从perl5.6起引入的perl的模块化编程概念,它通过package修饰字将perl程序变成一个一个的模块,从而实现单独功能的封装。好了,那么首先,我们要让我们的gtpl.pl变成一个package。实现起来非常简单,在合并后的gtplcom.pl的第一行加入packge genroute;就可以啦,是不是非常简单呢?当然,你可以把genroute改成任何你喜欢的名字,但是可不要用中文啊。
package也就是perl的模块里面的程序都是以子程序的方式存在的,所以下一步,我们要把gtpl.pl中原来的主程序部分变成子程序的模样。这就是我们的genroute子程序的模样啦。在子程序的最后一步,当然要记得返回我们计算好的路径。在一步中,唯一需要注意的就是全局变量和局部变量的处理,全局变量的定义要放在最外面,这样就可以了。- sub genroute
- {
- $src_area = shift;
- $dest_area = shift;
- $debug_enable = 0;
-
- $degree = 0;
- $debug_enable = shift;
- my $ret_val =0 ;
- # route_list is re-use in generate_path and search_path function
- @rout_list=();
- @rout_list_from=();
-
- @areaopen_list=();
- @areaopen_list_from=();
- @areaopen_list_prev=($src_area);
- @areaclose_list =($src_area);
-
- $degree = 1;
- generate_hash();
-
-
- # input parameter check
- ($ret_val,$src_area) = check_area_valid($src_area);
- if ($ret_val > 0)
- {
- print "start area $src_area illegal\n";
- exit;
- }
-
- ($ret_val,$dest_area) = check_area_valid($dest_area);
- if ($ret_val > 0)
- {
- print "dest area $dest_area illegal\n";
- exit;
- }
-
- search_path();
- generate_path();
- return($route_list);
- }
复制代码 代码的修改已经完成了,我们是不是可以试试可不可以生成.dll了呢?嗯,别急,还记得最早的时候在template.pl中的那些让perlctrl识别的程序片断么?我们可得修改一下它们让它们为gtplcom.pl服务,不然perlctrl可不知道它们是给gtplcom.pl用的呢。我把解释用中文和下面的代码写在一起了,请移步往下看。
=pod
=begin PerlCtrl
%TypeLib = (
PackageName => 'genroute',#修改名字和上面package修饰字取的一样
# DO NOT edit the next 3 lines.
TypeLibGUID => '{62C50D29-FF11-4796-80AC-8180E38D3313}', # do NOT edit this line
ControlGUID => '{EA2C7471-311F-42C9-BB87-087E895BD952}', # do NOT edit this line either
DispInterfaceIID=> '{E35C3806-100F-4C60-AAF2-EC8288921BA7}', # or this one
ControlName => 'genroute',#还是一样的名字
ControlVer => 1, #如果你在原有的.dll中增加了新的method,property然后更新了.dll,那么就把版本号加1
ProgID => 'goto.genroute',#这是最重要的一个名字啦,取个好记的名字,因为你在zmud中调用.dll的时候要用到它,它是.dll和.dll调用者之间的脐带
DefaultMethod => '',#这个默认的method在你创建com对象的时候会直接执行,用来初始化一些变量是最不错的了,不过在这里我们不使用它
Methods => {#方法是最重要的了,为了接收数据和传回数据,我们需要好好的声明我们的方法
'genroute' => {#这是方法的名字,也是在zmud中调用的时候会使用到的
RetType => VT_BSTR,#返回值的类型
TotalParams => 3,#传入参数的个数
NumOptionalParams => 1,#有多少个可选的参数
ParamList =>['src' => VT_BSTR,#第一个传入参数的名字和类型
'dest' => VT_BSTR,#第二个传入参数的名字和类型
'debug_en' => VT_BOOL, #第三个传入参数的名字和类型]
},
}, # end of 'Methods'
Properties => {
}
, # end of 'Properties'
); # end of %TypeLib
=end PerlCtrl
=cut
至此为止,所有代码修改工作都结束啦。激动人心的时刻到来了,让我们来看看我们.dll是不是能够如期生成。在命令行键入perlctrl gtplcom.pl,运行完毕后,gtplcom.dll被生成在与gtplcom.pl同一目录下。
可是这个.dll它是个二进制文件诶,我怎么知道它好不好用!别急,让我们先来注册它。使用regsvr32.exe gtplcom.dll就可以直接注册一个.dll了。一旦注册成功,就会有个欢乐的对话框弹出来告诉你成功啦。
这里提一下.dll的注册相关的内容。由于我安装的activelperl和perl dev kit都是32位的,所以生成的.dll是32位的,而我的机器是64位的,因此,使用的regsvr32的路径有所不同:
在64位机下,请使用c:\windows\sysWOW64\regsvr32.exe去注册32位的.dll,如果你的机器是32位机,那直接在命令行里键入regsvr32.exe gtplcom.dll即可。因为windows默认的regsvr32是路径是与32/64对应的。
注册.dll成功之后,就可以在zmud使用它了。为了将它与逍遥行无缝结合,我们把gt alias中routecreate处替换成:
- #VAR gtpl_route %comcreate( "goto.genroute")
- #var Route_list @gtpl_route.genroute(@CurrentPosition,%1,0)
- #t+ goto/walk
- step=1
- stepmax=%numitems( @Route_list)
- stepaccu=1
- #if (@walkmode=1) {set brief 1}
- walk
复制代码 现在,享受秒速的逍遥行吧。
附件:
gtplcom.pl 源码供参考
gtplcom.dll .dll供直接使用
perl516.dll 我没在没有安装perl的机器上测试过,如果你的机器上没安装perl,请将这个.dll和zmud.exe放在同一目录下
gt.txt 修改过的gt alias,直接覆盖逍遥行原gt alias即可
使用方法:
把gtplcom.dll和perl516.dll和zmud.exe放在同一目录下。注册gtplcom.dll。
在原来的逍遥行中删除原有gt alias导入gt.txt。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
|