执行Lua WINAPI代码而不显示shell

时间:2014-03-05 16:44:54

标签: shell winapi lua

我使用Lua创建了一个简单的Alien for Lua应用程序。它完美地运行,除了执行它时,Lua Shell也会显示。有没有办法“隐藏”这个shell,在后台运行它,关闭它等等,以便我只看到消息框?

代码:

require "luarocks.require"
require "alien"

local MessageBox = alien.User32.MessageBoxA
MessageBox:types{ret = "long", abi = "stdcall", "long", "string", "string", "long" }
MessageBox(0, "Hello World!", "My Window Title", 0x00000040)

当前输出:

Current Output

期望的输出:

Desired Output

4 个答案:

答案 0 :(得分:4)

TL;博士

将您的脚本重命名为hello.wlua,以便使用wlua.exe

详细

虽然很可能,如果详细,找到并关闭Windows提供过程的有问题的控制台窗口,如果该控制台永远不会出现在第一位,那将会更好。如果它确实出现,那么它可能会在屏幕上闪烁,并导致一些用户感到困惑。

子系统

从最早的日子开始,Windows就拥有了"subsystem"的概念,每个可执行文件都标识了这个概念。普通的GUI应用程序与/SUBSYSTEM:WINDOWS链接,并获得完整的GUI处理,包括在需要时创建和显示自己的窗口的责任。

期望从命令行(或批处理文件)运行的应用程序与/SUBSYSTEM:CONSOLE链接,因此具有保证打开的标准文件句柄,并且可能连接到某些控制台窗口(或管道,或重定向到文件,但它们确实存在)。这种保证足够强大,当控制台程序在控制台外启动时(如从Exporer双击,或在“开始”|“运行”框中命名),系统会自动为其创建控制台窗口,并绑定标准文件处理新控制台。

还有其他子系统,但这两个子系统是普通用户和开发人员唯一重要的子系统。

lua.exewlua.exe

那为什么这很重要?

将为控制台链接库存lua.exe,因为这样可以从命令提示符以交互方式使用。但是,这意味着即使您不想要控制台窗口,也始终会提供控制台窗口。

Lua for Windows发行版(从您在控制台标题栏中显示的路径名看起来就像您正在使用的那样)包含一个名为wlua.exe的第二个副本,该副本仅因为Windows子系统的链接而不同。因此,如果脚本显式创建一个要显示的窗口,它只显示一个窗口。当然,这也意味着它不能在命令提示符下以交互方式使用。

文件类型和关联

为方便起见,您可以将文件类型.wluawlua.exe相关联,并使用该文件类型命名GUI脚本。这将使得能够以通常的方式启动程序而无需获得额外的控制台。当然,在调试它们时,您总是可以在命令提示符下使用lua.exe运行它们,并利用stdout的存在和print函数的效用。

在我的电脑上(64位Win 7 Pro)我有以下关联,看起来它们是由安装Lua for Windows创建的:

C:...>assoc .lua
.lua=Lua.Script

C:...>ftype lua.script
lua.script="C:\Program Files (x86)\Lua\5.1\lua.exe" "%1" %*

C:...>assoc .wlua
.wlua=wLua.Script

C:...>ftype Wlua.script
Wlua.script="C:\Program Files (x86)\Lua\5.1\wlua.exe" "%1" %*

额外学分:PATHEXT

您还可以将.lua添加到PATHEXT环境变量,以便在命令提示符下保存键入文件类型。我目前没有这样配置,但过去曾这样做过。我发现使用相同文件类型命名模块和脚本的标准做法使得它不那么有用。

PATHEXT环境变量列出了在命名要运行的程序时将在PATH中搜索的文件类型,而不指定其文件类型。这方面的文档很难找到,因为似乎没有一个MSDN页面列出了所有“官方”环境变量及其用法。 This chapter of a book about Windows NT对PATH和PATHEXT的交互有一个很好的描述,尽管在某些方面巧妙地过时了,但它是我所遇到的命令提示符操作的最清楚的详细解释。

它澄清了PATH中的每个文件夹都会搜索PATHEXT中指定的每个扩展名:

  

如果命令名称包含文件扩展名,则shell会在每个目录中搜索命令名称指定的确切文件名。如果命令名称不包含文件扩展名,则shell会逐个添加PATHEXT环境变量中列出的扩展名,并在目录中搜索该文件名。请注意,shell会在特定目录中尝试所有可能的文件扩展名,然后再继续搜索下一个目录(如果有的话)。

它还记录了文件类型和关联如何与命令提示符交互。尽管它的年龄很大,但值得一读。

答案 1 :(得分:2)

Windows可执行文件明确列出了它们运行的​​子系统。由于Windows“lua.exe”链接到控制台子系统,因此Windows会自动为其创建控制台窗口。只需重新链接gui子系统的“lua.exe”,除非从控制台窗口运行,否则不会再看到输出。顺便说一句:Gui程序可以通过编程方式创建控制台。

另一种方法是在启动时关闭创建的控制台。 为此,您必须首先使用SetStdHandle重定向STDIN,STDOUT和STDERR(如果您根本不需要它,请使用打开到设备nul的文件),然后调用FreeConsole最终解除您不喜欢的控制台窗口。没有汗,你已经设置了“外星人”...

答案 2 :(得分:1)

如果你可以使用winapi module或在Alien中有类似的调用,你可以找到控制台窗口的处理程序并隐藏窗口本身。代码与此类似:

require winapi
local pid = winapi.get_current_pid()
local wins = winapi.find_all_windows(function(w)
  return w:get_process():get_pid() == pid
    and w:get_class_name() == 'ConsoleWindowClass'
end)
for _,win in pairs(wins) do win:show_async(winapi.SW_HIDE) end

您需要检查这是否使MessageBox可见。

答案 3 :(得分:1)

程序化解决方案(如果可能,在wlua.exe下运行相同的脚本)

do
   local i, j = 0, 0
   repeat j = j + 1 until not arg[j]
   repeat i = i - 1 until not arg[i-1]
   local exe = arg[i]:lower()
   -- check if the script is running under lua.exe
   if exe:find('lua%.exe$') and not exe:find('wlua%.exe$') then
      arg[i] = exe:gsub('lua%.exe$','w%0')
      -- check if wlua.exe exists
      if io.open(arg[i]) then  
         -- run the same script under wlua.exe
         os.execute('"start "" "'..table.concat(arg,'" "',i,j-1)..'""')
         -- exit right now to close console window
         os.exit()
      end
   end
end

-- Your main program is here:
require "luarocks.require"
require "alien"

local MessageBox = alien.User32.MessageBoxA
MessageBox:types{ret = "long", abi = "stdcall", "long", "string", "string", "long" }
MessageBox(0, "Hello World!", "My Window Title", 0x00000040)