Developing a Complex Native Windows Application in Lua

Matt Campbell, Lead Programmer, Serotek Corporation

Lua Workshop 2008, Washington, DC, July 2008

What Application?

Serotek Corporation's System Access product:

Available packages:

Why a Native Windows Application?

Why Lua?

Two Stages of Lua Adoption

Combination of Lua and Python

Rewrite of Python code in Lua

Demo

Summary of Challenges

Introducing LuaWin

LuaWin Conventions and Guidelines

Sample Windows API Function Binding

GetWindowThreadProcessId

static int wrap_GetWindowThreadProcessId(lua_State *L) {
  HWND hWnd;
  DWORD rv;
  DWORD dwProcessId;
  lua_settop(L, 1);
  hWnd = luawin_checkhwnd(L, 1);
  rv = GetWindowThreadProcessId(hWnd, &dwProcessId);
  if (rv == 0)
    luawin_error(L);
  luawin_pushdword(L, rv);
  luawin_pushdword(L, dwProcessId);
  return 2;
}

Strings

Goal: Ease Unicode support

Sample Windows API Function Binding with Strings

FindWindow

static int wrap_FindWindow(lua_State *L) {
  LPCTSTR lpClassName;
  LPCTSTR lpWindowName;
  HWND hWnd;
  lua_settop(L, 2);
  lpClassName = luawin_opttstring(L, 1, NULL);
  lpWindowName = luawin_opttstring(L, 2, NULL);
  hWnd = FindWindow(lpClassName, lpWindowName);
  if (hWnd == 0) {
    lua_pushnil(L);
    luawin_pusherror(L);
    return 2;
  } else {
    luawin_pushhwnd(L, hWnd);
    return 1;
  }
}

LuaWin COM Interface Bindings: Lua to COM

Example Usage of COM Interfaces from Lua

Microsoft Active Accessibility in Internet Explorer

local gti = winuser.GetGUIThreadInfo()
local hwnd = gti.hwndFocus
local acc =
  oleacc.AccessibleObjectFromWindow(hwnd, winuser.OBJID_CLIENT,
                                    oleacc.IID_IAccessible)
print(acc:get_accName(0))
local sp = acc:QueryInterface(servprov.IID_IServiceProvider)
local windowLW = sp:QueryService(mshtml.IID_IHTMLWindow2,
                                 oleauto.IID_IDispatch)
local window = combridge.toluacom(windowLW)
-- now work with the IE DOM...

Sample COM Interface Method Binding

IAccessible::get_accRole

static int wrap_IAccessible_get_accRole(lua_State *L) {
  IAccessible *self;
  VARIANT *pvarChild;
  VARIANT *pvarRole;
  HRESULT hr;
  lua_settop(L, 2);
  self = checkIAccessible(L, 1);
  pvarChild = luawin_newvariant(L);
  luawin_checkvariant(L, 2, pvarChild);
  pvarRole = luawin_newvariant(L);
  hr = self->lpVtbl->get_accRole(self, *pvarChild, pvarRole);
  if (hr == E_FAIL || !luawin_checkhr(L, hr))
    return 0;
  luawin_pushvariant(L, pvarRole);
  return 1;
}

LuaWin COM Gateways: COM to Lua

Sample Lua Implementation of a COM Method

IDocHostUIHandler::GetExternal

function UIHandler:GetExternal()
  local external, ifName = self.parent:getExternal()
  if not external then
    return false
  end
  local lcObj =
    luacom.ImplInterfaceFromTypelib(external, getResourceDLLPath(),
                                    ifName)
  return true, fromluacom(lcObj):QueryInterface(IID_IDispatch)
end

Sample COM Gateway Method

IBindStatusCallback::OnProgress

LUAWIN_GATEWAY_DEFINE_METHOD(IBindStatusCallback, OnProgress,
  (ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode,
   LPCWSTR szStatusText), ulProgress) {
  LUAWIN_GATEWAY_METHOD_ARG(ULONG, ulProgress);
  LUAWIN_GATEWAY_METHOD_ARG(ULONG, ulProgressMax);
  LUAWIN_GATEWAY_METHOD_ARG(ULONG, ulStatusCode);
  LUAWIN_GATEWAY_METHOD_ARG(LPCWSTR, szStatusText);
  luawin_pushulong(L, ulProgress);
  luawin_pushulong(L, ulProgressMax);
  luawin_pushulong(L, ulStatusCode);
  luawin_pushwstring(L, szStatusText);
  LUAWIN_GATEWAY_METHOD_CALL(0);
} LUAWIN_GATEWAY_METHOD_END

How Does it Really Work?

Refer to include/luawin_gateway.h in LuaWin

Unicode and Lua Standard Libraries: The Problem

Unicode and Lua Standard Libraries: My Solution

The utf8api library

Coroutines and C-to-Lua Callbacks: The Problem

Coroutines and C-to-Lua Callbacks: My Solution

New Lua C API function: lua_getmainthread

LUA_API lua_State *lua_getmainthread (lua_State *L) {
  lua_State *L1;
  lua_lock(L);
  L1 = G(L)->mainthread;
  lua_unlock(L);
  return L1;
}

Using the new function

C function in previous scenario should call lua_getmainthread and store the returned pointer

Lua versus Python: Python's Strengths

Lua versus Python: Lua's Strengths

Conclusion

Q&A