Task 038: .ASCX File Format

Task 038: .ASCX File Format

Task Response: .ASAX File Format Specifications and Implementation

The .ASAX file format, primarily associated with ASP.NET as the "ASP.NET Server Application File," is used to handle application-level and session-level events in ASP.NET web applications. The most common .ASAX file is Global.asax, which resides in the root directory of an ASP.NET application. Below, I address each part of the task based on the available information and critical analysis of the .ASAX file format.


1. Properties of the .ASAX File Format Intrinsic to Its File System

The .ASAX file format is a plain text file used by ASP.NET applications, typically written in a scripting language like C# or VB.NET. Its intrinsic properties, based on its role and structure within the ASP.NET framework, include:

  • File Extension: .asax
  • File Type: Text-based, human-readable script file.
  • Location: Stored in the root directory of an ASP.NET application, typically named Global.asax.
  • Purpose: Contains code to handle application-level and session-level events raised by ASP.NET or HTTP modules (e.g., Application_Start, Session_End).
  • Structure:
  • Application Directives: Tags at the start of the file (e.g., <%@ Application Language="C#" %>) that define settings for the ASP.NET parser.
  • Code Declaration Blocks: Server-side code within <script runat="server"> tags, typically in C# or VB.NET, defining event handlers.
  • Optional Nature: The file is optional; an ASP.NET application can function without it.
  • Security: Web servers are configured to reject direct URL requests to .ASAX files, preventing external access to the source code.
  • Runtime Behavior: Parsed and compiled at runtime into a .NET Framework class derived from HttpApplication.
  • MIME Type: application/x-aspx (though primarily associated with ASP.NET processing).
  • Encoding: Typically UTF-8, as it is a plain text file.
  • Single Instance: Only one .ASAX file (usually Global.asax) is allowed per ASP.NET application.
  • Event Handlers: Supports predefined event handlers such as:
  • Application_Start: Triggered when the application starts.
  • Application_End: Triggered when the application shuts down.
  • Session_Start: Triggered when a new user session begins.
  • Session_End: Triggered when a session ends (only for InProc session mode).
  • Application_Error: Triggered for unhandled exceptions.
  • Application_BeginRequest, Application_EndRequest, etc., for request lifecycle events.

These properties are derived from the file’s role in the ASP.NET framework and its interaction with the server’s file system.


Challenges and Assumptions

  • Lack of Formal Specification: Unlike formats like PNG or JSON, .ASAX does not have a publicly available, detailed file format specification beyond its role in ASP.NET. It is a text-based format with a structure defined by ASP.NET conventions.
  • Implementation Focus: The task requires reading, writing, and printing properties. Since .ASAX is a text file with code, “decoding” implies parsing its directives and code blocks, while “properties” refer to the intrinsic attributes listed above (e.g., extension, encoding, event handlers).
  • Cross-Language Implementation: The task requests implementations in Python, Java, JavaScript, and C. Since .ASAX files are plain text, the classes will focus on reading the file, parsing its structure (directives and event handlers), and writing back to a file.
  • C Language Note: C does not have a native "class" construct, so a struct-based approach with functions will be used to emulate class-like behavior.

Below, I provide implementations for each language, focusing on:

  • Reading the .ASAX file to extract its directives and event handlers.
  • Printing the identified properties to the console.
  • Writing a new .ASAX file with sample content.

2. Python Class for .ASAX File Handling

import re
import os

class ASAXFile:
    def __init__(self, filepath):
        self.filepath = filepath
        self.properties = {
            "extension": ".asax",
            "file_type": "text",
            "location": "root directory (Global.asax)",
            "encoding": "UTF-8",
            "directives": [],
            "event_handlers": [],
            "is_optional": True,
            "single_instance": True,
            "mime_type": "application/x-aspx"
        }

    def read(self):
        """Read and parse the .ASAX file."""
        try:
            with open(self.filepath, 'r', encoding='utf-8') as file:
                content = file.read()
                
                # Extract directives (e.g., <%@ Application Language="C#" %>)
                directive_pattern = r'<%@\s*(\w+)\s+([^%>]+)%>'
                directives = re.findall(directive_pattern, content)
                self.properties["directives"] = [(d[0], dict(re.findall(r'(\w+)="([^"]+)"', d[1]))) for d in directives]

                # Extract event handlers (e.g., Application_Start, Session_End)
                event_pattern = r'void\s+(\w+)\s*\([^)]*\)\s*{'
                self.properties["event_handlers"] = re.findall(event_pattern, content)
                
                return content
        except FileNotFoundError:
            print(f"Error: File {self.filepath} not found.")
            return None
        except Exception as e:
            print(f"Error reading file: {str(e)}")
            return None

    def write(self, content=None):
        """Write a sample .ASAX file."""
        try:
            sample_content = content or (
                '<%@ Application Language="C#" %>\n'
                '<script runat="server">\n'
                'void Application_Start(object sender, EventArgs e) {\n'
                '    // Code for application startup\n'
                '}\n'
                'void Application_Error(object sender, EventArgs e) {\n'
                '    // Code for error handling\n'
                '}\n'
                '</script>'
            )
            with open(self.filepath, 'w', encoding='utf-8') as file:
                file.write(sample_content)
            print(f"Successfully wrote to {self.filepath}")
        except Exception as e:
            print(f"Error writing file: {str(e)}")

    def print_properties(self):
        """Print all properties of the .ASAX file."""
        print("ASAX File Properties:")
        for key, value in self.properties.items():
            print(f"{key}: {value}")

# Example usage
if __name__ == "__main__":
    asax = ASAXFile("Global.asax")
    asax.write()  # Create a sample file
    asax.read()   # Read and parse the file
    asax.print_properties()  # Print properties

Explanation:

  • Reading: Uses regex to parse directives and event handlers from the text file.
  • Writing: Creates a sample Global.asax file with basic directives and event handlers if no content is provided.
  • Properties: Stores and prints intrinsic properties like extension, encoding, and parsed directives/event handlers.
  • Error Handling: Handles file not found and encoding errors.

3. Java Class for .ASAX File Handling

import java.io.*;
import java.util.*;
import java.util.regex.*;

public class ASAXFile {
    private String filepath;
    private Map<String, Object> properties;

    public ASAXFile(String filepath) {
        this.filepath = filepath;
        this.properties = new HashMap<>();
        properties.put("extension", ".asax");
        properties.put("file_type", "text");
        properties.put("location", "root directory (Global.asax)");
        properties.put("encoding", "UTF-8");
        properties.put("directives", new ArrayList<String[]>());
        properties.put("event_handlers", new ArrayList<String>());
        properties.put("is_optional", true);
        properties.put("single_instance", true);
        properties.put("mime_type", "application/x-aspx");
    }

    public String read() {
        try (BufferedReader reader = new BufferedReader(new FileReader(filepath))) {
            StringBuilder content = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }

            // Parse directives
            Pattern directivePattern = Pattern.compile("<%@\\s*(\\w+)\\s+([^%>]+)%>");
            Matcher directiveMatcher = directivePattern.matcher(content);
            List<String[]> directives = new ArrayList<>();
            while (directiveMatcher.find()) {
                directives.add(new String[]{directiveMatcher.group(1), directiveMatcher.group(2)});
            }
            properties.put("directives", directives);

            // Parse event handlers
            Pattern eventPattern = Pattern.compile("void\\s+(\\w+)\\s*\\([^)]*\\)\\s*\\{");
            Matcher eventMatcher = eventPattern.matcher(content);
            List<String> events = new ArrayList<>();
            while (eventMatcher.find()) {
                events.add(eventMatcher.group(1));
            }
            properties.put("event_handlers", events);

            return content.toString();
        } catch (FileNotFoundException e) {
            System.out.println("Error: File " + filepath + " not found.");
            return null;
        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
            return null;
        }
    }

    public void write(String content) {
        String defaultContent = content != null ? content :
                "<%@ Application Language=\"C#\" %>\n" +
                "<script runat=\"server\">\n" +
                "void Application_Start(object sender, EventArgs e) {\n" +
                "    // Code for application startup\n" +
                "}\n" +
                "void Application_Error(object sender, EventArgs e) {\n" +
                "    // Code for error handling\n" +
                "}\n" +
                "</script>";
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filepath))) {
            writer.write(defaultContent);
            System.out.println("Successfully wrote to " + filepath);
        } catch (IOException e) {
            System.out.println("Error writing file: " + e.getMessage());
        }
    }

    public void printProperties() {
        System.out.println("ASAX File Properties:");
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }

    public static void main(String[] args) {
        ASAXFile asax = new ASAXFile("Global.asax");
        asax.write(null); // Create a sample file
        asax.read();      // Read and parse
        asax.printProperties(); // Print properties
    }
}

Explanation:

  • Reading: Uses BufferedReader to read the file and regex to parse directives and event handlers.
  • Writing: Writes a sample Global.asax file with default content if none is provided.
  • Properties: Stores properties in a HashMap and prints them.
  • Error Handling: Manages file I/O exceptions.

4. JavaScript Class for .ASAX File Handling

const fs = require('fs').promises;

class ASAXFile {
    constructor(filepath) {
        this.filepath = filepath;
        this.properties = {
            extension: '.asax',
            file_type: 'text',
            location: 'root directory (Global.asax)',
            encoding: 'utf-8',
            directives: [],
            event_handlers: [],
            is_optional: true,
            single_instance: true,
            mime_type: 'application/x-aspx'
        };
    }

    async read() {
        try {
            const content = await fs.readFile(this.filepath, 'utf-8');

            // Parse directives
            const directivePattern = /<%@\s*(\w+)\s+([^%>]+)%>/g;
            const directives = [];
            let match;
            while ((match = directivePattern.exec(content)) !== null) {
                directives.push([match[1], match[2]]);
            }
            this.properties.directives = directives;

            // Parse event handlers
            const eventPattern = /void\s+(\w+)\s*\([^)]*\)\s*{/g;
            const events = [];
            while ((match = eventPattern.exec(content)) !== null) {
                events.push(match[1]);
            }
            this.properties.event_handlers = events;

            return content;
        } catch (error) {
            if (error.code === 'ENOENT') {
                console.error(`Error: File ${this.filepath} not found.`);
            } else {
                console.error(`Error reading file: ${error.message}`);
            }
            return null;
        }
    }

    async write(content) {
        const defaultContent = content || (
            '<%@ Application Language="C#" %>\n' +
            '<script runat="server">\n' +
            'void Application_Start(object sender, EventArgs e) {\n' +
            '    // Code for application startup\n' +
            '}\n' +
            'void Application_Error(object sender, EventArgs e) {\n' +
            '    // Code for error handling\n' +
            '}\n' +
            '</script>'
        );
        try {
            await fs.writeFile(this.filepath, defaultContent, 'utf-8');
            console.log(`Successfully wrote to ${this.filepath}`);
        } catch (error) {
            console.error(`Error writing file: ${error.message}`);
        }
    }

    printProperties() {
        console.log('ASAX File Properties:');
        for (const [key, value] of Object.entries(this.properties)) {
            console.log(`${key}: ${JSON.stringify(value, null, 2)}`);
        }
    }
}

// Example usage
(async () => {
    const asax = new ASAXFile('Global.asax');
    await asax.write(); // Create a sample file
    await asax.read();  // Read and parse
    asax.printProperties(); // Print properties
})();

Explanation:

  • Reading: Uses Node.js fs.promises for asynchronous file reading and regex to parse directives and event handlers.
  • Writing: Writes a sample Global.asax file using async I/O.
  • Properties: Stores properties in an object and prints them with proper formatting.
  • Error Handling: Handles file not found and I/O errors.

5. C Implementation for .ASAX File Handling

Since C does not support classes, I use a struct and functions to emulate class-like behavior.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LINE 1024
#define MAX_EVENTS 10
#define MAX_DIRECTIVES 10

typedef struct {
    const char *filepath;
    char *extension;
    char *file_type;
    char *location;
    char *encoding;
    char *directives[MAX_DIRECTIVES][2]; // [name, attributes]
    char *event_handlers[MAX_EVENTS];
    int directive_count;
    int event_count;
    int is_optional;
    int single_instance;
    char *mime_type;
} ASAXFile;

ASAXFile* create_asax_file(const char *filepath) {
    ASAXFile *asax = (ASAXFile*)malloc(sizeof(ASAXFile));
    asax->filepath = filepath;
    asax->extension = ".asax";
    asax->file_type = "text";
    asax->location = "root directory (Global.asax)";
    asax->encoding = "UTF-8";
    asax->directive_count = 0;
    asax->event_count = 0;
    asax->is_optional = 1;
    asax->single_instance = 1;
    asax->mime_type = "application/x-aspx";
    for (int i = 0; i < MAX_DIRECTIVES; i++) {
        asax->directives[i][0] = NULL;
        asax->directives[i][1] = NULL;
    }
    for (int i = 0; i < MAX_EVENTS; i++) {
        asax->event_handlers[i] = NULL;
    }
    return asax;
}

void free_asax_file(ASAXFile *asax) {
    for (int i = 0; i < asax->directive_count; i++) {
        free(asax->directives[i][0]);
        free(asax->directives[i][1]);
    }
    for (int i = 0; i < asax->event_count; i++) {
        free(asax->event_handlers[i]);
    }
    free(asax);
}

char* read_asax(ASAXFile *asax) {
    FILE *file = fopen(asax->filepath, "r");
    if (!file) {
        printf("Error: File %s not found.\n", asax->filepath);
        return NULL;
    }

    char *content = NULL;
    size_t size = 0;
    char line[MAX_LINE];
    while (fgets(line, MAX_LINE, file)) {
        size_t len = strlen(line);
        content = realloc(content, size + len + 1);
        strcpy(content + size, line);
        size += len;

        // Parse directives
        if (strstr(line, "<%@") && strstr(line, "%>")) {
            if (asax->directive_count < MAX_DIRECTIVES) {
                char *name = strtok(line + 3, " \t");
                char *attrs = strtok(NULL, "%>");
                asax->directives[asax->directive_count][0] = strdup(name);
                asax->directives[asax->directive_count][1] = strdup(attrs ? attrs : "");
                asax->directive_count++;
            }
        }

        // Parse event handlers
        if (strstr(line, "void ") && strstr(line, "(")) {
            if (asax->event_count < MAX_EVENTS) {
                char *name = strtok(line + 5, " \t(");
                asax->event_handlers[asax->event_count] = strdup(name);
                asax->event_count++;
            }
        }
    }

    fclose(file);
    return content;
}

void write_asax(ASAXFile *asax, const char *content) {
    const char *default_content = content ? content :
        "<%@ Application Language=\"C#\" %>\n"
        "<script runat=\"server\">\n"
        "void Application_Start(object sender, EventArgs e) {\n"
        "    // Code for application startup\n"
        "}\n"
        "void Application_Error(object sender, EventArgs e) {\n"
        "    // Code for error handling\n"
        "}\n"
        "</script>";

    FILE *file = fopen(asax->filepath, "w");
    if (!file) {
        printf("Error writing file: %s\n", asax->filepath);
        return;
    }

    fputs(default_content, file);
    fclose(file);
    printf("Successfully wrote to %s\n", asax->filepath);
}

void print_properties(ASAXFile *asax) {
    printf("ASAX File Properties:\n");
    printf("extension: %s\n", asax->extension);
    printf("file_type: %s\n", asax->file_type);
    printf("location: %s\n", asax->location);
    printf("encoding: %s\n", asax->encoding);
    printf("is_optional: %d\n", asax->is_optional);
    printf("single_instance: %d\n", asax->single_instance);
    printf("mime_type: %s\n", asax->mime_type);
    printf("directives:\n");
    for (int i = 0; i < asax->directive_count; i++) {
        printf("  %s: %s\n", asax->directives[i][0], asax->directives[i][1]);
    }
    printf("event_handlers:\n");
    for (int i = 0; i < asax->event_count; i++) {
        printf("  %s\n", asax->event_handlers[i]);
    }
}

int main() {
    ASAXFile *asax = create_asax_file("Global.asax");
    write_asax(asax, NULL); // Create a sample file
    char *content = read_asax(asax); // Read and parse
    print_properties(asax); // Print properties
    if (content) free(content);
    free_asax_file(asax);
    return 0;
}

Explanation:

  • Reading: Reads the file line by line, parsing directives and event handlers using string matching.
  • Writing: Writes a sample Global.asax file with default content.
  • Properties: Stores properties in a struct and prints them.
  • Memory Management: Properly allocates and frees memory for dynamic strings.
  • Error Handling: Checks for file opening failures.

Notes and Limitations

  • Parsing Simplicity: The parsing of directives and event handlers is basic (using regex in Python/Java/JavaScript and string matching in C) and assumes well-formed .ASAX files. Real-world parsing may require more robust handling for complex code.
  • File Access: The implementations assume local file system access. In a real ASP.NET environment, .ASAX files are server-side and may require additional permissions.
  • C Implementation: Lacks native regex support, so parsing is simplified. A library like PCRE could enhance it but was avoided for simplicity.
  • Cross-Platform: The JavaScript implementation requires Node.js for file system access. Browser-based JavaScript would need adjustments (e.g., File API).

If you need further refinements or have a specific .ASAX file to test, please provide it, and I can tailor the code further!

The .ASCX file format is used for ASP.NET User Controls, which are text-based files containing a mix of markup, directives, and code for reusable components in ASP.NET web applications. It is not a binary format requiring low-level decoding but rather a markup file parsed by the ASP.NET compiler. The "properties" intrinsic to the format refer to the attributes defined in the mandatory @Control directive at the start of the file, which configure how the control is compiled and behaves. There is no fixed binary structure or file system-specific metadata beyond standard file attributes; the format's structure is text-based, typically starting with <%@ Control ... %> followed by optional script blocks and HTML/ASP.NET markup.

1. List of all properties (attributes) of the .ASCX file format

Based on official ASP.NET documentation, the following is a comprehensive list of attributes supported in the @Control directive. These are the key configurable properties of the format:

  • AutoEventWireup: Boolean indicating whether events are automatically wired up (default: true).
  • ClassName: String specifying the class name for the dynamically compiled control (default: based on filename).
  • ClientIDMode: String specifying the algorithm for generating client IDs (e.g., "AutoID", default: Inherit).
  • CodeBehind: String path to the code-behind file (for compatibility with older ASP.NET versions; use CodeFile instead in modern versions).
  • CodeFile: String path to the source code-behind file, used with Inherits.
  • CodeFileBaseClass: String path to a base class for shared scenarios with code-behind.
  • CompilationMode: String indicating if the control should be compiled (e.g., "Always", default: Always).
  • CompilerOptions: String of compiler command-line switches.
  • Debug: Boolean indicating whether to compile with debug symbols (default: false).
  • Description: String providing a textual description (ignored by compiler).
  • EnableTheming: Boolean indicating whether themes are applied (default: true).
  • EnableViewState: Boolean indicating whether view state is maintained (default: true).
  • Explicit: Boolean for Visual Basic Option Explicit mode (default: false, ignored by other languages).
  • Inherits: String specifying the base class to inherit from.
  • Language: String specifying the programming language (e.g., "C#").
  • LinePragmas: Boolean indicating whether to generate line pragmas for debugging (default: false).
  • Src: String path to a linked source file for code.
  • Strict: Boolean for Visual Basic Option Strict mode (default: false, ignored by other languages).
  • TargetSchema: String specifying the schema for validation (optional, for older tools).

These attributes are parsed as key-value pairs in the directive (e.g., <%@ Control Language="C#" EnableViewState="false" %>). Not all are required; defaults apply where noted.

2. Python class for .ASCX files

import re
from typing import Dict, Optional

class AscxFileHandler:
    def __init__(self, file_path: str):
        self.file_path = file_path
        self.content: str = ""
        self.properties: Dict[str, str] = {}
        self._load()

    def _load(self) -> None:
        with open(self.file_path, 'r', encoding='utf-8') as f:
            self.content = f.read()
        self._parse_properties()

    def _parse_properties(self) -> None:
        # Find the @Control directive
        match = re.search(r'<%@\s*Control\s*(.*?)\s*%>', self.content, re.IGNORECASE | re.DOTALL)
        if match:
            directive = match.group(1)
            # Parse attributes: key="value" or key=value (handle quotes)
            attrs = re.findall(r'(\w+)\s*=\s*(?:"(.*?)"|(\S+))', directive)
            for key, val1, val2 in attrs:
                value = val1 if val1 else val2
                self.properties[key] = value

    def read_property(self, key: str) -> Optional[str]:
        return self.properties.get(key)

    def read_all_properties(self) -> Dict[str, str]:
        return self.properties.copy()

    def write_property(self, key: str, value: str) -> None:
        self.properties[key] = value
        self._update_content()

    def _update_content(self) -> None:
        # Rebuild directive
        new_directive = '<%@ Control ' + ' '.join(f'{k}="{v}"' for k, v in self.properties.items()) + ' %>'
        # Replace old directive in content
        self.content = re.sub(r'<%@\s*Control\s*.*?\s*%>', new_directive, self.content, flags=re.IGNORECASE | re.DOTALL, count=1)

    def save(self, output_path: Optional[str] = None) -> None:
        path = output_path or self.file_path
        with open(path, 'w', encoding='utf-8') as f:
            f.write(self.content)

3. Java class for .ASCX files

import java.io.*;
import java.util.*;
import java.util.regex.*;

public class AscxFileHandler {
    private String filePath;
    private String content;
    private Map<String, String> properties = new HashMap<>();

    public AscxFileHandler(String filePath) {
        this.filePath = filePath;
        load();
    }

    private void load() {
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
            }
            content = sb.toString();
            parseProperties();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void parseProperties() {
        Pattern pattern = Pattern.compile("<%@\\s*Control\\s*(.*?)\\s*%>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher matcher = pattern.matcher(content);
        if (matcher.find()) {
            String directive = matcher.group(1);
            Pattern attrPattern = Pattern.compile("(\\w+)\\s*=\\s*(\"(.*?)\"|(\\S+))");
            Matcher attrMatcher = attrPattern.matcher(directive);
            while (attrMatcher.find()) {
                String key = attrMatcher.group(1);
                String value = attrMatcher.group(3) != null ? attrMatcher.group(3) : attrMatcher.group(4);
                properties.put(key, value);
            }
        }
    }

    public String readProperty(String key) {
        return properties.get(key);
    }

    public Map<String, String> readAllProperties() {
        return new HashMap<>(properties);
    }

    public void writeProperty(String key, String value) {
        properties.put(key, value);
        updateContent();
    }

    private void updateContent() {
        StringBuilder newDirective = new StringBuilder("<%@ Control ");
        for (Map.Entry<String, String> entry : properties.entrySet()) {
            newDirective.append(entry.getKey()).append("=\"").append(entry.getValue()).append("\" ");
        }
        newDirective.append("%>");
        Pattern pattern = Pattern.compile("<%@\\s*Control\\s*.*?\\s*%>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        content = pattern.matcher(content).replaceFirst(newDirective.toString());
    }

    public void save(String outputPath) {
        if (outputPath == null) outputPath = filePath;
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputPath))) {
            writer.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4. JavaScript class for .ASCX files (Node.js)

const fs = require('fs');

class AscxFileHandler {
    constructor(filePath) {
        this.filePath = filePath;
        this.content = '';
        this.properties = {};
        this._load();
    }

    _load() {
        this.content = fs.readFileSync(this.filePath, 'utf-8');
        this._parseProperties();
    }

    _parseProperties() {
        const match = this.content.match(/<%@\s*Control\s*(.*?)\s*%>/is);
        if (match) {
            const directive = match[1];
            const attrs = directive.matchAll(/(\w+)\s*=\s*(?:"(.*?)"|(\S+))/g);
            for (const attr of attrs) {
                const key = attr[1];
                const value = attr[2] || attr[3];
                this.properties[key] = value;
            }
        }
    }

    readProperty(key) {
        return this.properties[key] || null;
    }

    readAllProperties() {
        return { ...this.properties };
    }

    writeProperty(key, value) {
        this.properties[key] = value;
        this._updateContent();
    }

    _updateContent() {
        let newDirective = '<%@ Control ';
        for (const [k, v] of Object.entries(this.properties)) {
            newDirective += `${k}="${v}" `;
        }
        newDirective += '%>';
        this.content = this.content.replace(/<%@\s*Control\s*.*?\s*%>/is, newDirective);
    }

    save(outputPath = null) {
        const path = outputPath || this.filePath;
        fs.writeFileSync(path, this.content, 'utf-8');
    }
}

5. C++ class for .ASCX files (using std::regex for parsing)

#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <regex>

class AscxFileHandler {
private:
    std::string filePath;
    std::string content;
    std::map<std::string, std::string> properties;

    void load() {
        std::ifstream file(filePath);
        if (file.is_open()) {
            std::string line;
            while (std::getline(file, line)) {
                content += line + "\n";
            }
            file.close();
            parseProperties();
        }
    }

    void parseProperties() {
        std::regex directiveRegex(R"(<%@\s*Control\s*(.*?)\s*%>)", std::regex::icase | std::regex::ECMAScript);
        std::smatch match;
        if (std::regex_search(content, match, directiveRegex)) {
            std::string directive = match[1].str();
            std::regex attrRegex(R"((\w+)\s*=\s*(?:"(.*?)"|(\S+)))");
            std::sregex_iterator iter(directive.begin(), directive.end(), attrRegex);
            std::sregex_iterator end;
            for (; iter != end; ++iter) {
                std::string key = (*iter)[1].str();
                std::string value = (*iter)[2].str().empty() ? (*iter)[3].str() : (*iter)[2].str();
                properties[key] = value;
            }
        }
    }

    void updateContent() {
        std::string newDirective = "<%@ Control ";
        for (const auto& pair : properties) {
            newDirective += pair.first + "=\"" + pair.second + "\" ";
        }
        newDirective += "%>";
        std::regex directiveRegex(R"(<%@\s*Control\s*.*?\s*%>)", std::regex::icase | std::regex::ECMAScript);
        content = std::regex_replace(content, directiveRegex, newDirective, std::regex_constants::format_first_only);
    }

public:
    AscxFileHandler(const std::string& fp) : filePath(fp) {
        load();
    }

    std::string readProperty(const std::string& key) {
        auto it = properties.find(key);
        return (it != properties.end()) ? it->second : "";
    }

    std::map<std::string, std::string> readAllProperties() {
        return properties;
    }

    void writeProperty(const std::string& key, const std::string& value) {
        properties[key] = value;
        updateContent();
    }

    void save(const std::string& outputPath = "") {
        std::string path = outputPath.empty() ? filePath : outputPath;
        std::ofstream file(path);
        if (file.is_open()) {
            file << content;
            file.close();
        }
    }
};