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. selenium模块用法详解

    selenium用法详解 selenium主要是用来做自动化测试,支持多种浏览器,爬虫中主要用来解决JavaScript渲染问题. 模拟浏览器进行网页加载,当requests,urllib无法正常获取 ...

  2. ListCtrl添加右键菜单(在对话框类中)

    在对话框类中如何添加NM_RCLICK消息: ListCtrl控件右键单击选择属性 在右侧属性栏中选择控件事件 在控件事件中找到NM_RCLICK并添加 完成,写代码

  3. Java 学习(16):集合框架

    Java 集合框架 早在Java 2中之前,Java就提供了特设类.比如:Dictionary, Vector, Stack,  Properties 这些类用来存储和操作对象组. 虽然这些类都非常有 ...

  4. 洛谷 P2026 求一次函数解析式

    P2026 求一次函数解析式 题目背景 做数学寒假作业的怨念…… 题目描述 给定两个整点的坐标,求它们所在直线的函数解析式(一次函数). 输入输出格式 输入格式: 输入共两行. 第一行有两个整数x1, ...

  5. [Javascript] Classify text into categories with machine learning in Natural

    In this lesson, we will learn how to train a Naive Bayes classifier or a Logistic Regression classif ...

  6. [Javascript] Different ways to create an new array/object based on existing array/object

    Array: 1. slice() const newAry = ary.slice() 2. concat const newAry = [].concat(ary) 3. spread oprea ...

  7. Incapsula免费日本CDN加速和CDNZZ香港CDN节点加速

    Incapsula免费日本CDN加速和CDNZZ香港CDN节点加速 免费的CDN对于那些将空间放在美国的博客网站加速效果是最好的,CDN可以解决国内连接美国的网络线路经常抽风和访问速度时好时坏的问题, ...

  8. rocketmq事务消息入门介绍

    说明 周五的时候发了篇:Rocketmq4.3支持事务啦!!!,趁着周末的时候把相关内容看了下,下面的主要内容就是关于RocketMQ事务相关内容介绍了. 说明: 今天这篇仅仅是入门介绍,并没有涉及到 ...

  9. telnet不是内部命令也不是外部命令

    转自:https://www.cnblogs.com/sishang/p/6600977.html 处理办法: 依次打开“开始”→“控制面板”→“打开或关闭Windows功能”,在打开的窗口处,寻找并 ...

  10. h5做app和原生app有什么区别?

    h5做app和原生app有什么区别? 一.总结 一句话总结: 二.h5做app和原生app有什么区别? 普通的HTML5技术与原生技术相比,有跨平台.动态.开放.直达二级内容页面等特点,但却在性能.工 ...