Portage de srlua vers une application graphique Windows avec Lua 5.3 sous MSVC

Paramétrage de la solution Visual Studio

L'intégration du moteur srlua dans l'écosystème Microsoft Developer Studio exige une orchestration précise des chemins de compilation et des dépendances binaire. Procédez ainsi :

  1. Chargez le fichier de solution racine et créez une configuration de build personnalisée.
  2. Clône les paramètres d'une configuration Release existante pour conserver les stratégies d'optimisation.
  3. Alignez les références entre les modules srglue-vc et srlua-vc. Un couplage strict est indispensable ; un mélange arbitraire de versions génère des échecs de liaison.

Pour orienter la construction vers la bibliothèque Lua 5.3, ouvrez les propriétés du projet principal. Dans la section VC++ Directories, saisissez les répertoires d'inclusion et les chemins vers les archives statiques. Au niveau du linker, remplacez systématiquement la dépendance lua54.lib par lua53.lib.

Basculer vers le sous-système Windows et adapter le point d'entrée

La transformaiton d'un binaire interactif en une application sans fenêtre de terminal nécessite des ajustements côté linker et dans le squelette C/C++ :

  • Sous Linker → System → SubSystem, appliquez Windows (/SUBSYSTEM:WINDOWS).
  • Dans Linker → Advanced → Entry Point, forcez mainCRTStartup afin que l'environnement d'exécution initialise correctement les buffers stdio et la pile CRT avant l'appel utilisateur.

La fonction d'entrée traditionnelle doit céder la place à WinMain. Sur l'API Win32, la récupération des flags de ligne de commande passe par GetCommandLineA puis nécessite un analyseur dédié. Voici une structure refactorisée :

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

int WINAPI WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow) 
{
    LPSTR rawInput = GetCommandLineA();
    int argCount = 0;
    char** parsedArgs = CommandLineToArgvA(rawInput, &argCount);

    if (!parsedArgs) return EXIT_FAILURE;

    lua_State* vm = luaL_newstate();
    if (!vm) {
        LocalFree(parsedArgs);
        return EXIT_FAILURE;
    }

    // Empilement des fonctions de contrôle et des arguments bruts
    lua_pushcfunction(vm, global_error_handler);
    lua_pushcfunction(vm, script_bootstrap);
    lua_pushinteger(vm, argCount);
    lua_pushlightuserdata(vm, parsedArgs);

    int execution_status = lua_pcall(vm, 2, 0, 1);
    if (execution_status != LUA_OK) {
        const char* failure_msg = lua_tostring(vm, -1);
        MessageBoxA(NULL, failure_msg, "Runtime Error", MB_ICONHAND);
    }

    lua_close(vm);
    LocalFree(parsedArgs);
    return EXIT_SUCCESS;
}

Générateur personnalisé de tableau d'arguments ANSI

L'absence native de CommandLineToArgvA dans certains socles SDK impose une passerelle explicite. L'algorithme ci-dessous convertit la chaîne brute en tableau Unicode intermédiaire, interroge l'API CommandLineToArgvW, puis reconstruit un tableau ANSI sécurisé en calculant précisément l'espace tampoun :

#include <windows.h>
#include <stdlib.h>
#include <assert.h>

char** BuildArgVectorFromNative(char* cmdSource, int* outputCount) {
    int wideChars = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, cmdSource, -1, NULL, 0);
    if (wideChars <= 0) return NULL;

    wchar_t* tempW = (wchar_t*)malloc(wideChars * sizeof(wchar_t));
    if (!tempW) return NULL;

    if (MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, cmdSource, -1, tempW, wideChars) <= 0) {
        free(tempW);
        return NULL;
    }

    int vectorSize = 0;
    wchar_t** wTokens = CommandLineToArgvW(tempW, &vectorSize);
    free(tempW);
    if (!wTokens || vectorSize == 0) return NULL;

    size_t allocatedMem = vectorSize * sizeof(char*);
    char* writeCursor = NULL;
    char** finalArray = NULL;

    // Pré-calcul de la capacité totale incluant les terminateurs nul
    for (int idx = 0; idx < vectorSize; ++idx) {
        size_t required = WideCharToMultiByte(CP_ACP, 0, wTokens[idx], -1, NULL, 0, NULL, NULL);
        allocatedMem += required;
    }

    finalArray = (char**)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, allocatedMem);
    if (!finalArray) goto release_and_fail;

    writeCursor = ((char*)finalArray) + (vectorSize * sizeof(char*));
    
    for (int idx = 0; idx < vectorSize; ++idx) {
        int written = WideCharToMultiByte(CP_ACP, 0, wTokens[idx], -1, 
            writeCursor, (int)(allocatedMem - (writeCursor - (char*)finalArray)), NULL, NULL);
        if (written <= 0) goto release_and_fail;

        finalArray[idx] = writeCursor;
        writeCursor += written;
    }

    *outputCount = vectorSize;
    LocalFree(wTokens);
    return finalArray;

release_and_fail:
    LocalFree(finalArray);
    LocalFree(wTokens);
    return NULL;
}

Cette implémetnation contourne les limites des anciennes chaines d'outils tout en respectant la sémantique de gestion mémoire Windows. Pour approfondir l'architecture des wrappers exécutables et la transmission des environnements de démarrage, consultez les analyses techniques de Antonio Scuri relatives à wlua.exe, lua.exe et srlua.exe.

Étiquettes: lua-5.3 srlua win32-api MSVC c-programmation

Publié le 4 septembre à 21h01