Files
Mush-Soundpack/cosmic rage/lua/luacom5.lua
2025-07-01 23:28:00 +03:00

1002 lines
24 KiB
Lua

--
-- 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("<html>\n")
io.write("<head>")
-- writes title
io.write("<title>")
if tlb.description then
io.write(tlb.description)
else
io.write(tlb.name)
end
io.write("</title>\n")
io.write("<h1>")
if tlb.description then
io.write(tlb.description)
else
io.write(tlb.name)
end
io.write(" Type Library")
io.write("</h1>\n")
io.write("<hr>\n")
--
-- First, makes an index for the entire type library
--
io.write("<h2>Summary</h2>\n")
-- Output function
local function write_typeinfo(i, typeinfo)
io.write("<li>")
io.write("<b>")
if typeinfo.type ~= "dispinterface" or typeinfo.dispatchable then
io.write("<a href=\"#"..typeinfo.name.."\">"..typeinfo.name.."</a>")
else
io.write(typeinfo.name)
end
io.write("</b>")
io.write("</li>")
if typeinfo.description and typeinfo.description ~= "" then
io.write(" - "..typeinfo.description)
end
io.write("</li>\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("<h3>Components</h3>\n")
filter_typeinfo("coclass")
io.write("<h3>Enumerations</h3>\n")
filter_typeinfo("enumeration")
io.write("<h3>Interfaces</h3>\n")
filter_typeinfo("dispinterface")
--
-- Now, describe each element
--
io.write("<hr>\n")
io.write("<h2>Detailed description</h2>\n")
-- describes coclasses
io.write("<h3>Components Classes</h3>\n")
local function describe_coclass(i, typeinfo)
assert(typeinfo.type == "coclass")
if typeinfo.restricted or typeinfo.hidden then
return
end
io.write("<h4>")
io.write("<a name=\""..typeinfo.name.."\"></a>")
io.write(typeinfo.name)
io.write("</h4>\n")
if typeinfo.description then
io.write(typeinfo.description.."<p>")
end
io.write("<pre>"..typeinfo.guid.."</pre><p>")
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("<a href=\"#"..default.name.."\">"..default.name.."</a>")
else
io.write(default.name)
end
io.write(" is the default interface for this component.<br>")
end
if source then
if source.dispatchable then
io.write("<a href=\"#"..source.name.."\">"..source.name.."</a>")
else
io.write(source.name)
end
io.write(" is the default set of events for this component.<br>")
end
if typeinfo.appobject then
io.write("This is the Application object.<br>")
end
if typeinfo.control then
io.write("This component is an OLE control.<br>")
end
if typeinfo.cancreate then
io.write("Instances of this component can be created.<br>")
end
end
map_function = describe_coclass
filter_typeinfo("coclass")
-- describes enumerations
io.write("<hr><h3>Enumerations</h3>\n")
local function describe_enum(i, typeinfo)
assert(typeinfo.type == "enumeration")
io.write("<h4>")
io.write("<a name=\""..typeinfo.name.."\"></a>")
io.write(typeinfo.name)
io.write("</h4>\n")
if typeinfo.description and typeinfo.description ~= nil then
io.write(typeinfo.description.."<p>")
end
local function describe_constant(i, constant)
io.write("<li>")
io.write(constant.name.." = "..tostring(constant.value))
io.write("</li>\n")
end
io.write("<ul>\n")
table.foreachi(typeinfo.values, describe_constant)
io.write("</ul>\n")
end
map_function = describe_enum
filter_typeinfo("enumeration")
-- describes interfaces
io.write("<hr><h3>Interfaces</h3>\n")
local function describe_interface(i, typeinfo)
assert(typeinfo.type == "dispinterface")
io.write("<h4>")
io.write("<a name=\""..typeinfo.name.."\"></a>")
io.write(typeinfo.name)
io.write("</h4>\n")
io.write("<pre>"..typeinfo.guid.."</pre><p>")
local function describe_method_use(method)
io.write("<b>LuaCOM examples:</b><br><br>")
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 .. "<br>")
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 .. "<br>")
io.write("com_obj:set" .. method.name .. "(" .. method.parameters[1].name .. ")<br>")
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(")<br>")
end
end
local function describe_method(i, method)
if method.rawMethod == nil then
return
end
io.write("<li>")
io.write(method.name)
if method.prototype then
io.write("<pre>"..method.prototype.."</pre>\n")
else
io.write("<br>")
end
if method.description then
io.write(method.description.."<br>\n")
end
describe_method_use(method)
io.write("<p>")
io.write("</li>\n")
end
io.write("<ul>\n")
table.foreachi(typeinfo.methods, describe_method)
io.write("</ul>\n")
end
map_function = describe_interface
filter_typeinfo("dispinterface")
io.write("</html>")
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