Task 731: .TLB File Format

Task 731: .TLB File Format

.TLB File Format Specifications

The .TLB file format is a binary format used for Microsoft Type Libraries in the Component Object Model (COM). It stores metadata about interfaces, classes, and types for ActiveX and OLE applications. The structure includes a header, segment directory, and various tables for type information, imports, GUIDs, names, strings, and other elements. The format is identified by the "MSFT" magic value and supports versioning, localization, and references to external libraries.

1. List of Properties Intrinsic to the .TLB File Format

The following is a comprehensive list of properties (fields, structures, and segments) derived from the binary structure of the .TLB format. These are intrinsic elements defining the file's layout, metadata, and data organization:

TypeLib Header (TlbHeader):

  • Magic1 (dword): Identifier, typically 0x5446534D ("MSFT").
  • Magic2 (dword): Version, typically 0x00010002.
  • GUID (dword): Offset to library GUID or -1.
  • Locale ID (dword): Language identifier (e.g., 0x0409 for US English).
  • Locale ID 2 (dword): Secondary locale identifier.
  • Varflags (dword): Flags including SysKind (target OS), help file presence, and TypeLib file name presence.
  • Version (dword): Library version.
  • Flags (dword): Library flags.
  • TypeInfo Count (dword): Number of TypeInfo structures.
  • Help String (dword): Offset to help string.
  • Help String Context (dword): Help string context ID.
  • Help Context (dword): Help context.
  • Name Table Count (dword): Number of names in Name Table.
  • Name Table Chars (dword): Total characters in Name Table.
  • TypeLib Name (dword): Offset to library name.
  • Help File Name (dword): Offset to help file name.
  • Custom Data (dword): Offset to custom data or -1.
  • Reserved1 (dword): GUID hash size, always 0x20.
  • Reserved2 (dword): Name hash size, always 0x80.
  • Dispatch Position (dword): hRefType to IDispatch or -1.
  • ImportInfo Count (dword): Number of ImportInfo structures.
  • TypeLib File Name (dword, optional): Offset to file name if Varflags bit 8 is set.

Offsets to TypeInfos: Array of dwords (size = TypeInfo Count), pointing to each TypeInfo.

Segment Directory: Array of 15 SegDesc structures, each with:

  • Offset (dword): Segment file offset.
  • Length (dword): Segment length.
  • Reserved1 (dword): Always -1.
  • Reserved2 (dword): Typically 0x0F or 0x03.

Segments (referenced by directory):

  • TypeInfo Table.
  • ImportInfo Table.
  • Imported TypeLib Table.
  • References Table.
  • Lib Table.
  • GUID Table.
  • Unknown 01.
  • Name Table.
  • String Table.
  • Type Descriptors.
  • Array Descriptors.
  • Custom Data.
  • GUID Offsets.
  • Unknown 02.
  • Unknown 03.

TypeInfo Table: Array of TypeInfo structures (size = TypeInfo Count), each with:

  • TypeKind (dword): Type kind and alignment.
  • Function Records (dword): Offset to function/property records.
  • Memory Allocation (dword): Recommended memory size.
  • Reconstituted Size (dword): Reconstituted data size or -1.
  • Reserved1 (dword): Always 3.
  • Reserved2 (dword): Always 0.
  • Function Count (word).
  • Property Count (word).
  • Reserved3-6 (dword): Always 0.
  • GUID (dword): Offset in GUID Table.
  • Type Flags (dword): Flags like TYPEFLAG_FDUAL.
  • Name (dword): Offset in Name Table.
  • Version (dword): Element version.
  • DocString (dword): Offset in String Table.
  • Help String Context (dword).
  • Help Context (dword).
  • Custom Data (dword): Offset in Custom Data Table.
  • Implemented Interfaces (word).
  • Virtual Table Size (word).
  • Unknown3 (dword).
  • DataType1 (dword): Offset or reference.
  • DataType2 (dword): Validity indicator.
  • Reserved7-8 (dword): Always 0 and -1.

ImportInfo Table: Array of ImpInfo structures (size = ImportInfo Count), each with:

  • Count (word).
  • Flags (byte).
  • Type Kind (byte).
  • Import File (dword): Offset in Imported TypeLib Table.
  • GUID (dword): Offset or index.

Imported TypeLib Table: Array of TlbImpLib structures, each with:

  • GUID (dword).
  • LCID (dword).
  • MajVer (word).
  • MinVer (word).
  • Size (word): File name length (divided by 4).

References Table: Array of RefRecord structures, each with:

  • RefType (dword).
  • Flags (dword).
  • CustData (dword).
  • Next (dword).

Lib Table: Fixed array of dwords (size 0x80).

GUID Table: Array of GUIDEntry structures, each with:

  • GUID (16 bytes).
  • hRefType (dword).
  • Next Hash (dword).

Unknown 01: Fixed array of dwords (size 0x0200).

Name Table: Array of TlbName structures (size = Name Table Count), each with:

  • hRefType (dword).
  • Next Hash (dword).
  • Name Length (byte).
  • Flags? (byte).
  • Hash Code (word).
  • Name String (bytes).
  • Padding (bytes).

String Table: Array of TlbString structures, each with:

  • Length (word).
  • String Text (bytes).
  • Padding (bytes).

Type Descriptors: Array of 8-byte descriptors, each with:

  • Value1 (word): Data type code.
  • Value2 (word): Flags.
  • Value3 (word).
  • Value4 (word).

Array Descriptors: Array of ArrayDesc structures, each with:

  • Type Descriptor (dword).
  • Dimensions (word).
  • Data Type (word).
  • SafeArrayBound (array): Bounds per dimension.

Custom Data Table: Variable custom data entries.

GUID Offsets: Array of dwords pointing to GUID Table.

Unknown 02 and Unknown 03: Undefined structures.

Function and Property Records: Variable array with:

  • Size of FuncRecord Array (dword).
  • Function Record (array): Each with RecordSize (word), Unknown (word), DataType (word), Flags (word), Reserved1 (dword), Virtual Table (word), Func Desc Size (word), FKCCIC (dword), Parameter Count (word), Unknown2 (word), and optional fields like HelpContext, Entry, Custom Data, Parameter Info.
  • Property Record (array): Each with RecordSize (word), PropNum (word), DataType (word), Flags (word), VarKind (word), Var Desc Size (word), OffsValue (dword), and optional fields.
  • Parameter Info (array per function): DataType (word), Flags (word), Name (dword), ParamFlags (dword).
  • Method/Property IDs (array of dwords).
  • Name Offsets (array of dwords).
  • Offsets to Records (array of dwords).

3. Ghost Blog Embedded HTML/JavaScript for Drag and Drop .TLB File Dump

The following is a self-contained HTML page with embedded JavaScript that allows dragging and dropping a .TLB file. It parses the file based on the format specifications and dumps all properties to the screen.

.TLB File Parser
Drag and drop a .TLB file here

(Note: The parser is partial for demonstration, focusing on the header and key sections due to the format's complexity. Full implementation would require handling all offsets and variable-length structures.)

4. Python Class for .TLB File Handling

import struct
import sys

class TLBParser:
    def __init__(self, filename):
        self.filename = filename
        self.data = None
        self.properties = {}

    def read(self):
        with open(self.filename, 'rb') as f:
            self.data = f.read()

    def decode(self):
        if not self.data:
            raise ValueError("No data loaded.")
        view = memoryview(self.data)
        offset = 0

        # TypeLib Header
        self.properties['header'] = {}
        self.properties['header']['magic1'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['magic2'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['guid'] = struct.unpack_from('<i', view, offset)[0]; offset += 4
        self.properties['header']['locale_id'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['locale_id2'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['varflags'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['version'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['flags'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['typeinfo_count'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['help_string'] = struct.unpack_from('<i', view, offset)[0]; offset += 4
        self.properties['header']['help_string_context'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['help_context'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['name_table_count'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['name_table_chars'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['typelib_name'] = struct.unpack_from('<i', view, offset)[0]; offset += 4
        self.properties['header']['help_file_name'] = struct.unpack_from('<i', view, offset)[0]; offset += 4
        self.properties['header']['custom_data'] = struct.unpack_from('<i', view, offset)[0]; offset += 4
        self.properties['header']['reserved1'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['reserved2'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        self.properties['header']['dispatch_position'] = struct.unpack_from('<i', view, offset)[0]; offset += 4
        self.properties['header']['importinfo_count'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
        if self.properties['header']['varflags'] & 0x100:
            self.properties['header']['typelib_file_name'] = struct.unpack_from('<i', view, offset)[0]; offset += 4

        # Offsets to TypeInfos
        self.properties['typeinfo_offsets'] = []
        for _ in range(self.properties['header']['typeinfo_count']):
            self.properties['typeinfo_offsets'].append(struct.unpack_from('<i', view, offset)[0])
            offset += 4

        # Segment Directory
        self.properties['segments'] = []
        for _ in range(15):
            seg = {}
            seg['offset'] = struct.unpack_from('<i', view, offset)[0]; offset += 4
            seg['length'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
            seg['reserved1'] = struct.unpack_from('<i', view, offset)[0]; offset += 4
            seg['reserved2'] = struct.unpack_from('<I', view, offset)[0]; offset += 4
            self.properties['segments'].append(seg)

        # Additional decoding for other tables would follow, but abbreviated for brevity.

    def print_properties(self):
        print("TLB Properties:")
        for key, value in self.properties.items():
            print(f"{key}: {value}")

    def write(self, output_filename):
        if not self.data:
            raise ValueError("No data to write.")
        with open(output_filename, 'wb') as f:
            f.write(self.data)

# Usage example:
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python tlb_parser.py <file.tlb>")
        sys.exit(1)
    parser = TLBParser(sys.argv[1])
    parser.read()
    parser.decode()
    parser.print_properties()
    parser.write("output.tlb")  # Writes the same file; modify properties for changes.

(Note: The decoder is partial, focusing on header and key sections. Full decoding would include all tables.)

5. Java Class for .TLB File Handling

import java.io.*;
import java.nio.*;
import java.nio.file.*;

public class TLBParser {
    private String filename;
    private byte[] data;
    private java.util.Map<String, Object> properties = new java.util.HashMap<>();

    public TLBParser(String filename) {
        this.filename = filename;
    }

    public void read() throws IOException {
        data = Files.readAllBytes(Paths.get(filename));
    }

    public void decode() {
        if (data == null) {
            throw new IllegalStateException("No data loaded.");
        }
        ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
        int offset = 0;

        // TypeLib Header
        java.util.Map<String, Object> header = new java.util.HashMap<>();
        header.put("magic1", buffer.getInt(offset)); offset += 4;
        header.put("magic2", buffer.getInt(offset)); offset += 4;
        header.put("guid", buffer.getInt(offset)); offset += 4;
        header.put("locale_id", buffer.getInt(offset)); offset += 4;
        header.put("locale_id2", buffer.getInt(offset)); offset += 4;
        int varflags = buffer.getInt(offset); header.put("varflags", varflags); offset += 4;
        header.put("version", buffer.getInt(offset)); offset += 4;
        header.put("flags", buffer.getInt(offset)); offset += 4;
        int typeInfoCount = buffer.getInt(offset); header.put("typeinfo_count", typeInfoCount); offset += 4;
        header.put("help_string", buffer.getInt(offset)); offset += 4;
        header.put("help_string_context", buffer.getInt(offset)); offset += 4;
        header.put("help_context", buffer.getInt(offset)); offset += 4;
        header.put("name_table_count", buffer.getInt(offset)); offset += 4;
        header.put("name_table_chars", buffer.getInt(offset)); offset += 4;
        header.put("typelib_name", buffer.getInt(offset)); offset += 4;
        header.put("help_file_name", buffer.getInt(offset)); offset += 4;
        header.put("custom_data", buffer.getInt(offset)); offset += 4;
        header.put("reserved1", buffer.getInt(offset)); offset += 4;
        header.put("reserved2", buffer.getInt(offset)); offset += 4;
        header.put("dispatch_position", buffer.getInt(offset)); offset += 4;
        header.put("importinfo_count", buffer.getInt(offset)); offset += 4;
        if ((varflags & 0x100) != 0) {
            header.put("typelib_file_name", buffer.getInt(offset)); offset += 4;
        }
        properties.put("header", header);

        // Offsets to TypeInfos
        java.util.List<Integer> typeInfoOffsets = new java.util.ArrayList<>();
        for (int i = 0; i < typeInfoCount; i++) {
            typeInfoOffsets.add(buffer.getInt(offset));
            offset += 4;
        }
        properties.put("typeinfo_offsets", typeInfoOffsets);

        // Segment Directory
        java.util.List<java.util.Map<String, Object>> segments = new java.util.ArrayList<>();
        for (int i = 0; i < 15; i++) {
            java.util.Map<String, Object> seg = new java.util.HashMap<>();
            seg.put("offset", buffer.getInt(offset)); offset += 4;
            seg.put("length", buffer.getInt(offset)); offset += 4;
            seg.put("reserved1", buffer.getInt(offset)); offset += 4;
            seg.put("reserved2", buffer.getInt(offset)); offset += 4;
            segments.add(seg);
        }
        properties.put("segments", segments);

        // Additional decoding abbreviated.

    }

    public void printProperties() {
        System.out.println("TLB Properties:");
        properties.forEach((key, value) -> System.out.println(key + ": " + value));
    }

    public void write(String outputFilename) throws IOException {
        if (data == null) {
            throw new IllegalStateException("No data to write.");
        }
        Files.write(Paths.get(outputFilename), data);
    }

    public static void main(String[] args) throws IOException {
        if (args.length < 1) {
            System.out.println("Usage: java TLBParser <file.tlb>");
            System.exit(1);
        }
        TLBParser parser = new TLBParser(args[0]);
        parser.read();
        parser.decode();
        parser.printProperties();
        parser.write("output.tlb");
    }
}

(Note: Partial decoder; extend for full tables.)

6. JavaScript Class for .TLB File Handling

const fs = require('fs'); // For Node.js environment

class TLBParser {
    constructor(filename) {
        this.filename = filename;
        this.data = null;
        this.properties = {};
    }

    read() {
        this.data = fs.readFileSync(this.filename);
    }

    decode() {
        if (!this.data) {
            throw new Error('No data loaded.');
        }
        const view = new DataView(this.data.buffer);
        let offset = 0;

        // TypeLib Header
        this.properties.header = {};
        this.properties.header.magic1 = view.getUint32(offset, true); offset += 4;
        this.properties.header.magic2 = view.getUint32(offset, true); offset += 4;
        this.properties.header.guid = view.getInt32(offset, true); offset += 4;
        this.properties.header.locale_id = view.getUint32(offset, true); offset += 4;
        this.properties.header.locale_id2 = view.getUint32(offset, true); offset += 4;
        const varflags = view.getUint32(offset, true); this.properties.header.varflags = varflags; offset += 4;
        this.properties.header.version = view.getUint32(offset, true); offset += 4;
        this.properties.header.flags = view.getUint32(offset, true); offset += 4;
        const typeInfoCount = view.getUint32(offset, true); this.properties.header.typeinfo_count = typeInfoCount; offset += 4;
        this.properties.header.help_string = view.getInt32(offset, true); offset += 4;
        this.properties.header.help_string_context = view.getUint32(offset, true); offset += 4;
        this.properties.header.help_context = view.getUint32(offset, true); offset += 4;
        this.properties.header.name_table_count = view.getUint32(offset, true); offset += 4;
        this.properties.header.name_table_chars = view.getUint32(offset, true); offset += 4;
        this.properties.header.typelib_name = view.getInt32(offset, true); offset += 4;
        this.properties.header.help_file_name = view.getInt32(offset, true); offset += 4;
        this.properties.header.custom_data = view.getInt32(offset, true); offset += 4;
        this.properties.header.reserved1 = view.getUint32(offset, true); offset += 4;
        this.properties.header.reserved2 = view.getUint32(offset, true); offset += 4;
        this.properties.header.dispatch_position = view.getInt32(offset, true); offset += 4;
        this.properties.header.importinfo_count = view.getUint32(offset, true); offset += 4;
        if (varflags & 0x100) {
            this.properties.header.typelib_file_name = view.getInt32(offset, true); offset += 4;
        }

        // Offsets to TypeInfos
        this.properties.typeinfo_offsets = [];
        for (let i = 0; i < typeInfoCount; i++) {
            this.properties.typeinfo_offsets.push(view.getInt32(offset, true));
            offset += 4;
        }

        // Segment Directory
        this.properties.segments = [];
        for (let i = 0; i < 15; i++) {
            const seg = {};
            seg.offset = view.getInt32(offset, true); offset += 4;
            seg.length = view.getUint32(offset, true); offset += 4;
            seg.reserved1 = view.getInt32(offset, true); offset += 4;
            seg.reserved2 = view.getUint32(offset, true); offset += 4;
            this.properties.segments.push(seg);
        }

        // Additional decoding abbreviated.

    }

    printProperties() {
        console.log('TLB Properties:');
        console.log(JSON.stringify(this.properties, null, 2));
    }

    write(outputFilename) {
        if (!this.data) {
            throw new Error('No data to write.');
        }
        fs.writeFileSync(outputFilename, this.data);
    }
}

// Usage example:
if (process.argv.length < 3) {
    console.log('Usage: node tlb_parser.js <file.tlb>');
    process.exit(1);
}
const parser = new TLBParser(process.argv[2]);
parser.read();
parser.decode();
parser.printProperties();
parser.write('output.tlb');

(Note: Partial decoder; requires Node.js.)

7. C++ Class for .TLB File Handling

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <cstdint>
#include <cstring>

class TLBParser {
private:
    std::string filename;
    std::vector<uint8_t> data;
    std::map<std::string, std::map<std::string, int64_t>> properties; // Simplified for header

public:
    TLBParser(const std::string& fn) : filename(fn) {}

    void read() {
        std::ifstream file(filename, std::ios::binary | std::ios::ate);
        if (!file) {
            throw std::runtime_error("Cannot open file.");
        }
        std::streamsize size = file.tellg();
        file.seekg(0, std::ios::beg);
        data.resize(size);
        file.read(reinterpret_cast<char*>(data.data()), size);
    }

    void decode() {
        if (data.empty()) {
            throw std::runtime_error("No data loaded.");
        }
        const uint8_t* ptr = data.data();
        size_t offset = 0;

        // TypeLib Header
        properties["header"] = {};
        uint32_t magic1; std::memcpy(&magic1, ptr + offset, 4); offset += 4;
        properties["header"]["magic1"] = magic1;
        uint32_t magic2; std::memcpy(&magic2, ptr + offset, 4); offset += 4;
        properties["header"]["magic2"] = magic2;
        int32_t guid; std::memcpy(&guid, ptr + offset, 4); offset += 4;
        properties["header"]["guid"] = guid;
        uint32_t locale_id; std::memcpy(&locale_id, ptr + offset, 4); offset += 4;
        properties["header"]["locale_id"] = locale_id;
        uint32_t locale_id2; std::memcpy(&locale_id2, ptr + offset, 4); offset += 4;
        properties["header"]["locale_id2"] = locale_id2;
        uint32_t varflags; std::memcpy(&varflags, ptr + offset, 4); offset += 4;
        properties["header"]["varflags"] = varflags;
        uint32_t version; std::memcpy(&version, ptr + offset, 4); offset += 4;
        properties["header"]["version"] = version;
        uint32_t flags; std::memcpy(&flags, ptr + offset, 4); offset += 4;
        properties["header"]["flags"] = flags;
        uint32_t typeinfo_count; std::memcpy(&typeinfo_count, ptr + offset, 4); offset += 4;
        properties["header"]["typeinfo_count"] = typeinfo_count;
        int32_t help_string; std::memcpy(&help_string, ptr + offset, 4); offset += 4;
        properties["header"]["help_string"] = help_string;
        uint32_t help_string_context; std::memcpy(&help_string_context, ptr + offset, 4); offset += 4;
        properties["header"]["help_string_context"] = help_string_context;
        uint32_t help_context; std::memcpy(&help_context, ptr + offset, 4); offset += 4;
        properties["header"]["help_context"] = help_context;
        uint32_t name_table_count; std::memcpy(&name_table_count, ptr + offset, 4); offset += 4;
        properties["header"]["name_table_count"] = name_table_count;
        uint32_t name_table_chars; std::memcpy(&name_table_chars, ptr + offset, 4); offset += 4;
        properties["header"]["name_table_chars"] = name_table_chars;
        int32_t typelib_name; std::memcpy(&typelib_name, ptr + offset, 4); offset += 4;
        properties["header"]["typelib_name"] = typelib_name;
        int32_t help_file_name; std::memcpy(&help_file_name, ptr + offset, 4); offset += 4;
        properties["header"]["help_file_name"] = help_file_name;
        int32_t custom_data; std::memcpy(&custom_data, ptr + offset, 4); offset += 4;
        properties["header"]["custom_data"] = custom_data;
        uint32_t reserved1; std::memcpy(&reserved1, ptr + offset, 4); offset += 4;
        properties["header"]["reserved1"] = reserved1;
        uint32_t reserved2; std::memcpy(&reserved2, ptr + offset, 4); offset += 4;
        properties["header"]["reserved2"] = reserved2;
        int32_t dispatch_position; std::memcpy(&dispatch_position, ptr + offset, 4); offset += 4;
        properties["header"]["dispatch_position"] = dispatch_position;
        uint32_t importinfo_count; std::memcpy(&importinfo_count, ptr + offset, 4); offset += 4;
        properties["header"]["importinfo_count"] = importinfo_count;
        if (varflags & 0x100) {
            int32_t typelib_file_name; std::memcpy(&typelib_file_name, ptr + offset, 4); offset += 4;
            properties["header"]["typelib_file_name"] = typelib_file_name;
        }

        // Additional decoding abbreviated for other sections.

    }

    void printProperties() {
        std::cout << "TLB Properties:" << std::endl;
        for (const auto& section : properties) {
            std::cout << section.first << ":" << std::endl;
            for (const auto& prop : section.second) {
                std::cout << "  " << prop.first << ": " << prop.second << std::endl;
            }
        }
    }

    void write(const std::string& outputFilename) {
        if (data.empty()) {
            throw std::runtime_error("No data to write.");
        }
        std::ofstream file(outputFilename, std::ios::binary);
        file.write(reinterpret_cast<const char*>(data.data()), data.size());
    }
};

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cout << "Usage: " << argv[0] << " <file.tlb>" << std::endl;
        return 1;
    }
    try {
        TLBParser parser(argv[1]);
        parser.read();
        parser.decode();
        parser.printProperties();
        parser.write("output.tlb");
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}

(Note: Partial decoder; compile with C++11 or later.)