-- -- Enhanced functionality for the LuaCOM library -- -- startup code: presumes LuaCOM has already been initialized -- and lies in the luacom table assert(luacom) luacomE = luacom -- tests for other dependencies assert(table) assert(string) assert(io) -- -- ExportConstants -- -- Exports all the constants defined in the type library -- to the global environment -- function luacomE.ExportConstants(obj, const_table) if luacomE.GetType(obj) == "LuaCOM" then obj = luacom.GetTypeInfo(obj) end if luacomE.GetType(obj) == "ITypeInfo" then obj = obj:GetTypeLib() end assert(luacomE.GetType(obj) == "ITypeLib") if const_table == nil then const_table = _G end obj:ExportConstants(const_table) end -- -- Proxies for luacom.CreateObject -- function luacomE.CreateLocalObject(ID) return luacom.CreateObject(ID, "local_server") end function luacomE.CreateInprocObject(ID) return luacom.CreateObject(ID, "inproc_server") end -- -- luacomE.pairs -- -- Returns an iterator for a COM enumerator -- function luacomE.pairs(luacom_obj) assert(luacom_obj) local enumerator = luacom.GetEnumerator(luacom_obj) if enumerator == nil then error("Could not get an enumerator") return end local function iterator(state, index) local value = state:Next() if value == nil then return nil else return index+1, value end end return iterator, enumerator, 0 end -- -- GetType -- -- Returns the type of the object (if it is managed by LuaCOM) -- function luacomE.GetType(obj) local typetable = getmetatable(obj) if typetable ~= nil then return typetable.type else return nil end end ------------------------------- -- Type library related code -- ------------------------------- -- Copies fields of a table to another function luacomE._copyFields(dest, src, fields) local function copyField(i,field) local src_field, dest_field if type(field) == "table" then src_field = field[2] dest_field = field[1] else src_field = field dest_field = field end if src[src_field] ~= "" then dest[dest_field] = src[src_field] end end table.foreach(fields, copyField) end -- FillTypeInfo -- -- Creates a table filled with the -- type information contained in a typeinfo function luacomE.FillTypeInfo(rawTypeInfo) if rawTypeInfo.FilledTypeInfo then return rawTypeInfo.FilledTypeInfo end local doc, attr local typeinfo = {} rawTypeInfo.FilledTypeInfo = typeinfo -- Basic information doc = rawTypeInfo:GetDocumentation() typeinfo.name = doc.name typeinfo.description = doc.helpstring -- Now the attributes attr = rawTypeInfo:GetTypeAttr() typeinfo.type = attr.typekind typeinfo.guid = attr.GUID -- copies flags table.foreach(attr.flags, function(i,v) typeinfo[i] = v end) -- function to fill the different types of elements local function fillMethods(methods, num_methods) local i, index, method, rawmethod index = 1 for i = 0, num_methods - 1 do method = {} rawmethod = rawTypeInfo:GetFuncDesc(i) if rawmethod ~= nil then method.rawMethod = rawmethod fields = {"name", "description", "helpfile", "helpcontext", {"dispid", "memid"}, {"typeinv", "invkind"}, {"num_params", "Params"}, "parameters", "type" } luacomE._copyFields(method, rawmethod, fields) local prototype if method.type then prototype = method.type.." "..method.name prototype = prototype.."(" end -- builds prototype if method.parameters then -- builds parameter list local first_param = true local function add_param(i, param) if first_param then first_param = false else prototype = prototype..", " first_param = false end if param.type then prototype = prototype..param.type.." " end prototype = prototype..param.name end table.foreachi(method.parameters, add_param) end if prototype then prototype = prototype..")" method.prototype = prototype end methods[index] = method index = index+1 end end end local function fillEnum(values, num_values) local i, rawConstant local fields = {"name", "value"} for i = 0, num_values - 1 do rawConstant = rawTypeInfo:GetVarDesc(i) constant = {} constant.rawConstant = rawConstant luacomE._copyFields(constant, rawConstant, fields) values[i+1] = constant end end local function fillCoClass(interfaces, num_interfaces) local i, interface, rawinterface, typeflags for i = 0, num_interfaces - 1 do rawinterface = rawTypeInfo:GetImplType(i) interface = {} interface.dispinterface = luacomE.FillTypeInfo(rawinterface) -- copies impltypeflags typeflags = rawTypeInfo:GetImplTypeFlags(i) table.foreach(typeflags, function(i,v) interface[i] = v end) interfaces[i+1] = interface end end -- Creates tables to hold components of the typeinfo if attr.typekind == "dispinterface" then typeinfo.methods = {} fillMethods(typeinfo.methods, attr.Funcs) elseif attr.typekind == "coclass" then typeinfo.interfaces = {} fillCoClass(typeinfo.interfaces, attr.ImplTypes) elseif attr.typekind == "enumeration" then typeinfo.values = {} fillEnum(typeinfo.values, attr.Vars) end return typeinfo end -- -- FillTypeLib -- -- Loads a type library and -- creates a table that rearranges -- the information contained in the -- table library to ease its navigation -- function luacomE.FillTypeLib(rawtlb) if rawtlb._luacom_isfilled then return rawtlb end -- Stores type library information local tlb = {} tlb._luacom_isfilled = true tlb.rawtlb = rawtlb local doc = rawtlb:GetDocumentation() tlb.name = doc.name tlb.description = doc.helpstring -- stores typeinfos local typeinfo, rawtypeinfo, attr for i = 0, rawtlb:GetTypeInfoCount() - 1 do rawTypeInfo = rawtlb:GetTypeInfo(i) typeinfo = luacomE.FillTypeInfo(rawTypeInfo) tlb[i+1] = typeinfo end return tlb end -- -- DumpTypeLib -- Creates a html page describing a type library, -- function luacomE.DumpTypeLib(obj, htmlfile) local tlb, rawtlb if type(obj) == "string" then rawtlb = luacom.LoadTypeLibrary(obj) elseif luacomE.GetType(obj) == "ITypeLib" then rawtlb = obj elseif luacomE.GetType(obj) == "LuaCOM" then obj = luacom.GetTypeInfo(obj) rawtlb = obj:GetTypeLib() elseif luacomE.GetType(obj) == "ITypeInfo" then rawtlb = obj:GetTypeLib() end if rawtlb == nil then error("Type library not found.") end tlb = luacomE.FillTypeLib(rawtlb) rawtlb = nil if htmlfile == nil then htmlfile = tlb.name..".html" elseif string.sub(htmlfile, -1) == "\\" then htmlfile = htmlfile..tlb.name..".html" end local filehandle = io.open(htmlfile, "w") if filehandle == nil then error("Could not create "..htmlfile..": file exists") end io.output(filehandle) -- writes html header io.write("\n") io.write("") -- writes title io.write("") if tlb.description then io.write(tlb.description) else io.write(tlb.name) end io.write("\n") io.write("

") if tlb.description then io.write(tlb.description) else io.write(tlb.name) end io.write(" Type Library") io.write("

\n") io.write("
\n") -- -- First, makes an index for the entire type library -- io.write("

Summary

\n") -- Output function local function write_typeinfo(i, typeinfo) io.write("
  • ") io.write("") if typeinfo.type ~= "dispinterface" or typeinfo.dispatchable then io.write(""..typeinfo.name.."") else io.write(typeinfo.name) end io.write("") io.write("
  • ") if typeinfo.description and typeinfo.description ~= "" then io.write(" - "..typeinfo.description) end io.write("\n") end -- filter function local function filter_typeinfo(type) local function filter(i,typeinfo) if typeinfo.type == type then map_function(i, typeinfo) end end table.foreachi(tlb, filter) end map_function = write_typeinfo io.write("

    Components

    \n") filter_typeinfo("coclass") io.write("

    Enumerations

    \n") filter_typeinfo("enumeration") io.write("

    Interfaces

    \n") filter_typeinfo("dispinterface") -- -- Now, describe each element -- io.write("
    \n") io.write("

    Detailed description

    \n") -- describes coclasses io.write("

    Components Classes

    \n") local function describe_coclass(i, typeinfo) assert(typeinfo.type == "coclass") if typeinfo.restricted or typeinfo.hidden then return end io.write("

    ") io.write("") io.write(typeinfo.name) io.write("

    \n") if typeinfo.description then io.write(typeinfo.description.."

    ") end io.write("

    "..typeinfo.guid.."

    ") local i, default, source -- locates the default interface and the source interface for i=1, table.getn(typeinfo.interfaces) do if typeinfo.interfaces[i].source and source == nil then source = typeinfo.interfaces[i].dispinterface elseif typeinfo.interfaces[i].source and typeinfo.interfaces[i].default then source = typeinfo.interfaces[i].dispinterface end if not typeinfo.interfaces[i].source and default == nil then default = typeinfo.interfaces[i].dispinterface elseif typeinfo.interfaces[i].default and not typeinfo.interfaces[i].source then default = typeinfo.interfaces[i].dispinterface end end if default then if default.dispatchable then io.write(""..default.name.."") else io.write(default.name) end io.write(" is the default interface for this component.
    ") end if source then if source.dispatchable then io.write(""..source.name.."") else io.write(source.name) end io.write(" is the default set of events for this component.
    ") end if typeinfo.appobject then io.write("This is the Application object.
    ") end if typeinfo.control then io.write("This component is an OLE control.
    ") end if typeinfo.cancreate then io.write("Instances of this component can be created.
    ") end end map_function = describe_coclass filter_typeinfo("coclass") -- describes enumerations io.write("


    Enumerations

    \n") local function describe_enum(i, typeinfo) assert(typeinfo.type == "enumeration") io.write("

    ") io.write("") io.write(typeinfo.name) io.write("

    \n") if typeinfo.description and typeinfo.description ~= nil then io.write(typeinfo.description.."

    ") end local function describe_constant(i, constant) io.write("

  • ") io.write(constant.name.." = "..tostring(constant.value)) io.write("
  • \n") end io.write("\n") end map_function = describe_enum filter_typeinfo("enumeration") -- describes interfaces io.write("

    Interfaces

    \n") local function describe_interface(i, typeinfo) assert(typeinfo.type == "dispinterface") io.write("

    ") io.write("") io.write(typeinfo.name) io.write("

    \n") io.write("
    "..typeinfo.guid.."

    ") local function describe_method_use(method) io.write("LuaCOM examples:

    ") local function param_lister() local first_param = true local function add_param(i, param) if param["in"] or (not param.out) then local name if param.default then local default = tostring(param.default) if default == "" then default = "?" end name = param.name .. "=" .. default else name = param.name end if param.opt then name = "[" .. name .. "]" end if first_param then io.write(name) first_param = false else io.write(", " .. name) end end end return add_param end local function retval_lister(first_retval) local has_retval = false return function(i, param) if param.out then if first_retval then io.write(param.name) first_retval = false else io.write(", " .. param.name) end has_retval = true end end, function() return has_retval end end if method.typeinv == "propget" then if method.num_params == 0 then io.write("com_obj." .. method.name .. "
    ") io.write("com_obj:get" .. method.name .. "()") else io.write("com_obj:get" .. method.name .. "(") table.foreachi(method.parameters, param_lister()) io.write(")") end elseif method.typeinv == "propput" then if method.num_params == 1 then io.write("com_obj." .. method.name .. " = " .. method.parameters[1].name .. "
    ") io.write("com_obj:set" .. method.name .. "(" .. method.parameters[1].name .. ")
    ") else io.write("com_obj:set" .. method.name .. "(") table.foreachi(method.parameters, param_lister()) io.write(")") end else if method.type ~= "void" then io.write("retval") table.foreachi(method.parameters, retval_lister(false)) io.write(" = ") else local lister, checker = retval_lister(true) table.foreachi(method.parameters, lister) if checker() then io.write(" = ") end end io.write("com_obj:" .. method.name .. "(") table.foreachi(method.parameters, param_lister()) io.write(")
    ") end end local function describe_method(i, method) if method.rawMethod == nil then return end io.write("

  • ") io.write(method.name) if method.prototype then io.write("
    "..method.prototype.."
    \n") else io.write("
    ") end if method.description then io.write(method.description.."
    \n") end describe_method_use(method) io.write("

    ") io.write("

  • \n") end io.write("\n") end map_function = describe_interface filter_typeinfo("dispinterface") io.write("") io.output():close() return htmlfile end -- -- shows TypeLib dump to the user -- function luacomE.ViewTypeLib(obj) local filename = os.getenv("TEMP") if filename == nil then filename = os.getenv("TMP") end if filename == nil then filename = luacom.GetCurrentDirectory() end if string.sub(filename, -1) ~= "\\" then filename = filename.."\\" end filename = luacomE.DumpTypeLib(obj, filename) local browser = luacom.CreateObject("InternetExplorer.Application") browser.Visible = true browser:Navigate2(filename) return filename end local interface_proto = { constants = {}, typedefs = {}, properties = {}, methods = {} } --[[function interface_proto:AddConstant(type, name, value) table.insert(self.constants, { type = type, name = name, value = value }) end]]-- --[[function interface_proto:AddTypedef(type, typedef) table.insert(self.typedefs, { type = type, typedef = typedef }) end]]-- function interface_proto:AddMethod(methodinfo) table.insert(self.methods, methodinfo) end function interface_proto:AddProperty(propertyinfo) table.insert(self.properties, propertyinfo) end function interface_proto:Write(file) local start_id = 0 local function attribute_writer(line_break) local first_attribute = true return function(name, value) if not first_attribute then if line_break then file:write(",\n ") else file:write(", ") end else if line_break then file:write(" ") end end first_attribute = false if(type(name) == "number") then file:write(value) else file:write(name .. "(" .. value .. ")") end end end local function constant_writer(i, constant) file:write("\n" .. " const " .. constant.type .. " " .. constant.name .. " = " .. constant.value .. ";\n") end local function typedef_writer(i, typedef) file:write("\n" .. " typedef " .. typedef.type .. " " .. typedef.typedef .. ";\n") end local function param_writer() local first_param = true return function(i, param) if not first_param then file:write(",\n ") end first_param = false if param.attributes then file:write("[") table.foreach(param.attributes, attribute_writer(false)) file:write("] ") end file:write(param.type .. " " .. param.name) end end local function property_writer(i, property) if not property.attributes then property.attributes = { id = tostring(start_id + i) } end file:write("\n [\n") if property.attributes.id == nil then property.attributes.id = tostring(start_id + i) end table.foreach(property.attributes, attribute_writer(true)) file:write("\n ]\n ") file:write(property.type .. " " .. property.name .. ";\n") end local function method_writer(i, method) if not method.attributes then method.attributes = { id = tostring(start_id + i) } end file:write("\n [\n") if method.attributes.id == nil then method.attributes.id = tostring(start_id + i) end table.foreach(method.attributes, attribute_writer(true)) file:write("\n ]\n ") if method.type then file:write(method.type) else file:write("void") end file:write(" " .. method.name .. "(") if method.parameters then file:write("\n ") table.foreachi(method.parameters, param_writer()) file:write("\n );\n") else file:write("void" .. ");\n") end end if self.attributes then file:write("[\n ") table.foreach(self.attributes, attribute_writer(true)) file:write("\n]\n") end file:write("dispinterface " .. self.name .. "\n{") if self.interface ~= nil then file:write(" interface " .. self.interface) else -- table.foreachi(self.constants, constant_writer) -- file:write("\n") -- table.foreachi(self.typedefs, typedef_writer) file:write("\n properties:\n") table.foreachi(self.properties, property_writer) start_id = table.getn(self.properties) file:write("\n methods:\n") table.foreachi(self.methods, method_writer) end file:write("\n};\n\n") end local coclass_proto = { interfaces = {} } function coclass_proto:AddInterface(attributes) table.insert(self.interfaces, attributes) end function coclass_proto:Write(file) local function attribute_writer(line_break) local first_attribute = true return function(name, value) if not first_attribute then if line_break then file:write(",\n ") else file:write(", ") end end first_attribute = false if(type(name) == "number") then file:write(value) else file:write(name .. "(" .. value .. ")") end end end local function interface_writer(i, interface) local name = interface.name interface.name = nil file:write("[") table.foreach(interface, attribute_writer(false)) file:write("] ") file:write("dispinterface " .. name .. ";\n") interface.name = name end if self.attributes then file:write("[\n ") table.foreach(self.attributes, attribute_writer(true)) file:write("\n]\n") end file:write("coclass " .. self.name .. "\n{\n") table.foreachi(self.interfaces, interface_writer) file:write("};\n\n") end local library_proto = { interfaces = {}, coclasses = {}, imports = {}, typedefs = {}, AddTypedef = interface_proto.AddTypedef } function library_proto:AddInterface(attributes) local name = attributes.name attributes.name = nil local interface = attributes.interface attributes.interface = nil local newinterface = { name = name, attributes = attributes, interface = interface, __index = interface_proto } setmetatable(newinterface, newinterface) table.insert(self.interfaces, newinterface) if interface ~= nil then return nil else return newinterface end end function library_proto:AddCoclass(attributes) local name = attributes.name attributes.name = nil local newcoclass = { name = name, attributes = attributes, __index = coclass_proto } setmetatable(newcoclass, newcoclass) table.insert(self.coclasses, newcoclass) return newcoclass end function library_proto:AddImport(library) table.insert(self.imports, library) end function library_proto:WriteODL(filename) local file = io.open(filename .. ".odl","w+") local function attribute_writer(line_break) local first_attribute = true return function(name, value) if not first_attribute then if line_break then file:write(",\n ") else file:write(", ") end end first_attribute = false if(type(name) == "number") then file:write(value) else file:write(name .. "(" .. value .. ")") end end end local function import_writer(i, import) file:write(" " .. "importlib(\"" .. import .. "\");\n") end local function typedef_writer(i, typedef) file:write("\n" .. " typedef " .. typedef.type .. " " .. typedef.typedef .. ";\n") end local function interface_writer(i, interface) interface:Write(file) end local function coclass_writer(i, coclass) coclass:Write(file) end if self.attributes then file:write("[\n ") table.foreach(self.attributes, attribute_writer(true)) file:write("\n]\n") end file:write("library " .. self.name .. "\n{\n") table.foreachi(self.imports, import_writer) table.foreachi(self.typedefs, typedef_writer) table.foreachi(self.interfaces, interface_writer) table.foreachi(self.coclasses, coclass_writer) file:write("};\n\n") file:close() end function library_proto:WriteTLB(filename) self:WriteODL(filename) os.execute("midl " .. filename .. ".odl") end function luacomE.NewLibrary(attributes) local name = attributes.name attributes.name = nil local newlibrary = { name = name, attributes = attributes, __index = library_proto } setmetatable(newlibrary, newlibrary) return newlibrary end