1. 这几天研究了一下lua,主要关注的是lua和vc之间的整合,把代码都写好放在VC宿主程序里,然后在lua里调用宿主程序的这些代码(或者叫接口、组件,随便你怎么叫),希望能用脚本来控制主程序的行为。这实际上也是一种把业务分离,用脚本控制的架构,可能有些人把这种脚本叫做业务引擎,工作流等。
  2. 为什么选择lua?
  3. 因为它是一个能和C/C++结合得很紧的脚本语言,而我们的程序是用VC++ 写的;另外一点是因为它的名气,连WOW都用lua来提供API让玩家修改其游戏行为,那我是找不到什么理由拒绝它了。
  4. Lua是什么?在哪里获取LUA?
  5. 详细的不说了,在网上一搜大把,只说一下它的官网吧:www.lua.org,在这里可以查到lua的应用,lua发布的版本,我用的是5.1.4,下载的是源代码的版本。
  6. LUA和VC MFC的整合?
  7. 1、 包含LUA:要使用LUA,当然要先把它包含进我们的工程里,可以有lib/dll方式、也可以用静态lib方式,当然也可以把整个lua的代码放进我们的工程,然后编译,因为lua只有几百K,很小。。。包含整个代码的方法我说一下:
  8. a) 在VC MFC新建一个工程(例如Dialog base工程)
  9. b) 去到工程里的文件tab页,新建一个文件夹,然后把所有lua里的.c、.h文件包含进来,注意有几个不用包含,lua.c、wmain.c、luac.c,包含进来之后,选中这个文件夹下面的所有.c文件,然后右键选setting,选择Not using precompiler file,
  10. 在VS2008中,右键.CPP文件,
  11. --->属性---->C\C++----->预编译头----->创建/使用预编译头----->选择 :不使用预编译头
  12. 这步做完就马上编译一下,应该是没问题的了!
  13. c) 还有动态库和静态lib两种方式把lua包含进工程里的,自己可以尝试一下。
  14. 2、 在某个地方(我是在MFCLua1Dlg.cpp文件开始处)包含lua的头文件,并声明一个lua_state指针,如下:
  15. extern "C"
  16. {
  17. #include "lua.h"
  18. #include "lualib.h"
  19. #include "lauxlib.h"
  20. }
  21. lua_State *lua;
  22. 在某个适当的地方(我是在OnInitDialog里)调用下面一段代码,这段代码的作用是打开一些必要的库:
  23. lua = lua_open ();
  24. if(lua)
  25. {
  26. luaopen_base (lua);
  27. luaopen_table (lua);
  28. luaopen_string (lua);
  29. luaopen_math (lua);
  30. luaopen_debug (lua);
  31. //luaopen_io (lua);
  32. }
  33. 用完lua的时候,调用下面一句来关闭lua库:
  34. lua_close (lua);
  35. 好了,到现在为止,lua已经完全变成我们程序的一部分了,试着编译一下,看看能不能顺利通过。。。
  36. LUA和MFC的交互?
  37. Lua变成我们程序的一部分之后,我们还要使用它,要记住我们的目标是用脚本程序控制我们宿主程序的执行流程,那我们就要完成两步,一是用mfc程序调用lua的函数,二是用lua调用mfc的函数,下面的内容对于初学者可能会开始有点难理解了,请打醒十二分精神,我会尽量简单的说。。。
  38. 1、 mfc调用lua的函数,这里用到一个StackDump的函数,是关于主程序和lua的交互栈的问题,下面会对交互栈的问题专门说明。
  39. 首先我们用记事本建立一个test.lua,内容是一个相加函数:
  40. function add ( x, y )
  41. return x + y;
  42. end
  43. 然后再VC里调用它,如下的一段代码,看这段代码的时候,先把StackDump函数忽略,只需要知道它是一个输出lua和vc交互栈内容的函数,对了,你可以新建一个button的click函数,然后把这段代码放进去:
  44. StackDump(lua);
  45. luaL_dofile(lua, "test.lua");     // 解释分析lua文件
  46. StackDump(lua);
  47. lua_getglobal(lua, "add");       // 取到一个全局标号add,取的同时会把add函数压栈
  48. StackDump(lua);
  49. lua_pushnumber(lua, 1);        // 把第一个参数压入栈里
  50. StackDump(lua);
  51. lua_pushnumber(lua, 2);        // 第二个参数压栈
  52. StackDump(lua);
  53. //lua_call(lua, 2, 1);
  54. if(lua_pcall(lua, 2, 1, 0) != 0)        // 执行add函数
  55. {
  56. AfxMessageBox("lua_pcall error!");
  57. return;
  58. }
  59. StackDump(lua);
  60. int d = (int)lua_tonumber(lua, -1);        // 函数执行完了,执行结果被压栈,所以取得最顶端的一个数就是结果值,-1就是指取栈顶的值
  61. CString str;
  62. str.Format("%d", d);
  63. AfxMessageBox(str);
  64. StackDump(lua);
  65. lua_pop(lua, 1);      // 把值从栈里清除,pop(弹出)一个值
  66. StackDump(lua);
  67. 好好分析一下这段代码,我们大概知道调用lua函数的一个过程是:dofile--〉函数名压栈--〉参数依序压栈--〉lua_pcall执行(执行结果压栈)--〉取出执行结果(如果有多个,就从栈里取出多个。。。),这样我们就能很轻松的调用到lua里的函数,其实就是要知道栈里发生了什么。。。
  68. 2、 lua调用MFC函数,比如我们想在lua里调用一个Msg函数,能弹出一个窗口来显示我们想显示的字串,然后返回值是1个"MsgOK!"字串。
  69. lua文件是这样的,第一句是调用Msg函数,第二句是测试返回的字串是不是"MsgOK!":
  70. c = Msg ("123");
  71. Msg(c);
  72. MFC程序里是这样的:
  73. 首先写一个将要被导出的函数,很多文章叫它为粘合函数(glue function):
  74. static int Msg(lua_State* L)
  75. {
  76. const char *s1 = luaL_checkstring(L, 1);     // 测试第一个参数是否为字串形式,并取得这个字串
  77. StackDump(L);
  78. MessageBox(NULL, s1, "caption", MB_OK);
  79. lua_pop(lua, 1);      // 清除栈里的这个字串
  80. StackDump(L);
  81. lua_pushlstring(L, "MsgOK!", 6);  // 把返回值压进栈里
  82. // 这个返回是指返回值的个数
  83. return 1;
  84. }
  85. 然后就导出这个函数,如下:
  86. lua_pushcfunction(lua, Msg);
  87. lua_setglobal(lua, "Msg");
  88. 接着就执行刚才的lua文件就行了,记得执行之前要先lua_open () 哦:
  89. luaL_dofile(lua, "test.lua");
  90. 运行的结果就是连续跳出两个messagebox,第一个是123,第二个是"MsgOK!",说明我们返回的字串被lua接收到了,lua的第二行我们没有接收它的返回值,则这个返回值会自动被抛弃了。
  91. 如果需要多返回值,则我们要把下面一句:
  92. lua_pushlstring(L, "MsgOK!", 6);  // 把返回值压进栈里
  93. // 这个返回是指返回值的个数
  94. return 1;
  95. 改为:
  96. lua_pushlstring(L, "MsgOK!", 6);  // 把返回值压进栈里
  97. lua_pushlstring(L, "haha!", 5);      // 把返回值压进栈里
  98. // 这个返回是指返回值的个数
  99. return 2;
  100. 这样我们在lua文件里就可以像下面一样取得两个返回值了:
  101. c,d = Msg("123");
  102. 那c和d就分别是"MsgOK!"和"haha!"两个字串了。 这种自动机制用起来还是比较方便的。
  103. 3、交互栈
  104. 上面两个调用其实都是对lua栈的实用,那我们就要好好理解一个概念,lua和vc的交互栈(栈是什么?请参考数据结构的书哈。。。)lua和vc就是通过这个栈来实现交互的,这个栈的访问函数有lua_gettop,lua_settop,lua_tostring,lua_toXXX等等的函数,我们要清楚当一个函数调用发生的时候,栈里是发生了什么。上面我用了一个StackDump函数,当我们调用的时候,能很清楚的看到栈里发生了什么。
  105. 首先我们要知道从栈顶往下数就是-1、-2,从栈底往上数就是1、2。
  106. 如果使用lua_gettop(L, 1),就是取得栈底第一个元素。lua_gettop(L, -1)就是取得栈顶的第一个元素。lua_pop() (L, 1)就是把栈顶的一个元素弹出来,lua_pop()(L, 2)就是把栈顶的两个元素弹出。
  107. 好了,写了一通,最后是这个StackDump函数的实现:
  108. int StackDump(lua_State* L)
  109. {
  110. int nTop = lua_gettop(L); //得到栈的元素个数。栈顶的位置。
  111. OutputDebugString("The Length of stack is %d\n", nTop); //输出栈顶位置
  112. for (int i = 1; i <= nTop; ++i)
  113. {
  114. int t = lua_type(L, i);
  115. OutputDebugString("%s:", lua_typename(L, t)); //这里的typename是把类型的枚举变成字符串,是类型名。不是栈中的位置。
  116. switch(t)
  117. {
  118. case LUA_TNUMBER:
  119. OutputDebugString("%f", lua_tonumber(L, i));
  120. break;
  121. case LUA_TSTRING:
  122. OutputDebugString("%s", lua_tostring(L, i));
  123. break;
  124. case LUA_TTABLE:
  125. //OutputDebugString("%s\n", lua_tostring(L,i));
  126. break;
  127. case LUA_TFUNCTION:
  128. //OutputDebugString("%s\n", lua_tostring(L,i));
  129. break;
  130. case LUA_TNIL:
  131. OutputDebugString("Is NULL");
  132. break;
  133. case LUA_TBOOLEAN:
  134. OutputDebugString("%s", lua_toboolean(L, i) ? "true" : "false");
  135. break;
  136. default:
  137. break;
  138. }
  139. OutputDebugString("\n");
  140. }
  141. return 0;
  142. }
  143. 本篇文章主要讲了lua和VC的整合、把LUA源代码和VC工程一起编译,VC调用LUA的代码,LUA调用VC的代码,返回值以及多个返回值、交互栈、输出交互栈里的元素信息等内容,下一篇将会说说如何避免阻塞的脚本,lua和多线程的使用等内容。
 
 

http://blog.csdn.net/chinazhd/article/details/7268910

LUA整合进MFC代码的更多相关文章

  1. hibernate整合进spring后的事务处理

    单独使用hibernate处理事务 本来只用hibernate开发,从而可以省了DAO层实现数据库访问和跨数据库,也可以对代码进行更好的封装,当我们web中单独使用hibernate时,我们需要单独的 ...

  2. 出售一套Unity + Lua热更新框架代码

    出售一套Unity + Lua的客户端框架代码,功能有资源管理.网络通信.配置文件解析.热更新.文件读写.Lua加密揭秘.UI框架.打包工具.编辑器工具等,已经在多个实际项目(已上线)中使用.代码优雅 ...

  3. 一键自动发布ipa(更新svn,拷贝资源,压缩资源,加密图片资源,加密数据文件,加密lua脚本,编译代码,ipa签名,上传ftp)

    一键自动发布ipa(更新svn,拷贝资源,压缩资源,加密图片资源,加密数据文件,加密lua脚本,编译代码,ipa签名,上传ftp) 程序员的生活要一切自动化,更要幸福^_^. 转载请注明出处http: ...

  4. 想做一个整合开源安全代码扫描工具的代码安全分析平台 - Android方向调研

    想做一个整合开源安全代码扫描工具的代码安全分析平台 - Android方向调研 http://blog.csdn.net/testing_is_believing/article/details/22 ...

  5. JavaScript怎么把对象里的数据整合进另外一个数组里

    https://blog.csdn.net/qq_26222859/article/details/70331833 var json1 = [ {"guoshui":[ 3000 ...

  6. 持续集成之②:整合jenkins与代码质量管理平台Sonar并实现构建失败邮件通知

    持续集成之②:整合jenkins与代码质量管理平台Sonar并实现构建失败邮件通知 一:Sonar是什么?Sonar 是一个用于代码质量管理的开放平台,通过插件机制,Sonar 可以集成不同的测试工具 ...

  7. springboot整合mybatis,redis,代码(二)

    一 说明: springboot整合mybatis,redis,代码(一) 这个开发代码的复制粘贴,可以让一些初学者直接拿过去使用,且没有什么bug 二 对上篇的说明 可以查看上图中文件: 整个工程包 ...

  8. Redis+LUA整合使用

    .前言 从本章节开始我们就开始讲解一些 Redis 的扩展应用了,之前讲的主从.哨兵和集群都相当重要,也许小公司用不到集群这么复杂的架构,但是也要了解各知识点的原理,只要了解了原理,无论什么时候是有, ...

  9. 整理代码,将一些曾经用过的功能整合进一个spring-boot

    一 由于本人的码云太多太乱了,于是决定一个一个的整合到一个springboot项目里面. 附上自己的项目地址https://github.com/247292980/spring-boot 功能 1. ...

随机推荐

  1. 父子margin塌陷

    1.使用padding 2.给父级使用border 3.给父级添加属性 overflow:hidden 4.浮动 5.定位{absolute,fixed} 6.伪元素代码 .parent:before ...

  2. Http协议的断点续传下载器,使用观察者模式监视下载进度,使用xml保存下载进度。

    下载使用Http协议,为了做到断点续传,在点击暂停后,将已下载的大小等数据通过Json存入xml中,当继续传输的时候从xml文件中读取大小继续下载(好几个月前写的,真的想不起来了) bool CHtt ...

  3. 【MongoDB】The connection between two tables

    In mongoDB, there are two general way to connect with two tables. Manual Connection and use DBRef 1. ...

  4. CentOS卸载Apache方法

    https://www.kafan.cn/edu/49420412.html CentOS卸载Apache方法 首先关闭httpd服务 /etc/init.d/httpd stop 列出httpd相关 ...

  5. dot-files/directories 点开头的文件或文件夹(windows/linux)

    What's so special about directories whose names begin with a dot? 不管是 windows 系统,还是类 linux 系统,以点开头的文 ...

  6. php编译参数注释

    1. 指定安装路径 --prefix=PREFIX 2. 指定运行用户 --with-fpm-user=nginx 3. 指定运行组 --with-fpm-group=nginx 3.与'--pref ...

  7. 含有打印、统计DataGridView(2)

    /// <summary>        /// 导出数据到Excel                /// </summary>        public void loa ...

  8. 洛谷——P1096 Hanoi双塔问题

    https://www.luogu.org/problem/show?pid=1096 题目描述 给定A.B.C三根足够长的细柱,在A柱上放有2n个中间有孔的圆盘,共有n个不同的尺寸,每个尺寸都有两个 ...

  9. JS实现动画的四条优化方法

    JS实现动画的四条优化方法 1)如果使用的是setTimeout实现的轮询动画,在每一次执行方法之前需要把前面的设置的定时器清除掉 2)为了防止全局变量的污染,我们把定时器的返回值赋值给当前操作元素的 ...

  10. 为SSO 5.5恢复忘记的administrator@vsphere.local密码

    转自:http://blog.itpub.net/27042095/viewspace-1179938/ 1. cd \Program Files\VMware\Infrastructure\VMwa ...