Lua Run Script Hidden - no command window.

sutex

New Member
This Lua script will produce a window pop-up and can kick me out of full-screen mode (gaming) when executed.
The ReplayStart.vbs file below the lua script does not show a command window when run by itself.
How can I make the Lua script run without any windows popping up?

Lua:
obs = obslua
start_script_name = '"C:\\Program Files\\obs-studio\\data\\obs-plugins\\frontend-tools\\scripts\\ReplayStart.vbs"'
end_script_name = '"C:\\Program Files\\obs-studio\\data\\obs-plugins\\frontend-tools\\scripts\\ReplayEnd.vbs"'

function on_event(event)
    if event == obs.OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED
        then io.popen(start_script_name)
    elseif event == obs.OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED
        then io.popen(end_script_name)
    end
end

function script_load(settings)
  obs.obs_frontend_add_event_callback(on_event)
end

Does not show a command window when run by itself.
Code:
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run chr(34) & "C:\\Program Files\\obs-studio\\data\\obs-plugins\\frontend-tools\\scripts\\start.bat" & Chr(34), 0
Set WshShell = Nothing

Reference script https://obsproject.com/forum/threads/obs-lua-on_event_replay_buffer-not-working.175417/post-646656
 

sutex

New Member
Another attempt to hide the command-line window.
Code:
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run "cmd /c VMRemote.exe http://192.168.xx.xx:8080/ExecuteMacro=71a320aa-24e4-4f3a-ad17-85058226f402/fc38e4c6-134d-434a-8d78-56d4898aed30", 0, False
Set WshShell = Nothing
 

sutex

New Member
I tried Python; the command window still opens, and the script turns the buffer on, then immediately turns it off, like a lua script did with using os.execute, I've run out of ideas :)
Python:
import obspython as obs

def on_event(event):
    if event == obs.OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED:
        subprocess.run(["cmd", "/c", "VMRemote.exe", "http://192.xx.xx.xxx:8080/ExecuteMacro=71a320aa-24e4-4f3a-ad17-85058226f402/fc38e4c6-134d-434a-8d78-56d4898aed30"])
    elif event == obs.OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED:
        subprocess.run(["cmd", "/c", "VMRemote.exe", "http://192.xx.xx.xxx:8080/ExecuteMacro=71a320aa-24e4-4f3a-ad17-85058226f402/fb83b52a-79d8-4e50-9c2c-c377a950665d"])

def script_load(settings):
    obs.obs_frontend_add_event_callback(on_event)
 

Suslik V

Active Member
As soon as your util supports HTTP requests I think it is better to use HTTP GET request instead of hiding cmd window.
There are number of ways to make it possible.

Example (only tested in ancient W7x64 machine, old OBS, and VoiceMacro 1.4).
"GET" request from Lua script on Windows (uses only libs shipped with OBS and OS):
Lua:
local obs = obslua

local ffi = require("ffi")
local winhttp = ffi.load("winhttp")
local kernel32 = ffi.load("kernel32")

ffi.cdef[[
 typedef void* HINTERNET;
 typedef unsigned int INTERNET_PORT;

 typedef const wchar_t* LPCWSTR;
 typedef unsigned long DWORD;
 typedef unsigned long DWORD_PTR;
 typedef DWORD* LPDWORD;
 typedef void* LPVOID;
 typedef int BOOL;

 
 static const int INTERNET_DEFAULT_PORT          = 0;
 static const int INTERNET_DEFAULT_HTTP_PORT     = 80;
 static const int INTERNET_DEFAULT_HTTPS_PORT    = 443;

 /* flags for WinHttpOpenRequest */
 static const int WINHTTP_FLAG_SECURE            = 0x00800000;

 HINTERNET WinHttpOpen(LPCWSTR pwszUserAgent, DWORD dwAccessType, LPCWSTR pwszProxyName, LPCWSTR pwszProxyBypass, DWORD dwFlags);
 HINTERNET WinHttpConnect(HINTERNET hSession, LPCWSTR pswzServerName, INTERNET_PORT nServerPort, DWORD dwReserved);
 HINTERNET WinHttpOpenRequest(HINTERNET hConnect, LPCWSTR pwszVerb, LPCWSTR pwszObjectName, LPCWSTR pwszVersion, LPCWSTR pwszReferrer, LPCWSTR *ppwszAcceptTypes, DWORD dwFlags);
 BOOL WinHttpSendRequest(HINTERNET hRequest, LPCWSTR lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, DWORD_PTR dwContext);
 BOOL WinHttpReceiveResponse(HINTERNET hRequest, LPVOID lpReserved);
 BOOL WinHttpQueryDataAvailable(HINTERNET hRequest, LPDWORD lpdwNumberOfBytesAvailable);
 BOOL WinHttpCloseHandle(HINTERNET hInternet);
 DWORD GetLastError(void);

 typedef unsigned int UINT;
 typedef const char* LPCSTR;
 typedef wchar_t* LPWSTR;
 typedef char* LPSTR;
 typedef int* LPBOOL;
 
 int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar);
 int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar);
 
/*  Code Page Default Values */
static const int CP_ACP                  = 0;           // default to ANSI code page
static const int CP_OEMCP                = 1;           // default to OEM  code page
static const int CP_MACCP                = 2;           // default to MAC  code page
static const int CP_THREAD_ACP           = 3;           // current thread's ANSI code page
static const int CP_SYMBOL               = 42;          // SYMBOL translations

static const int CP_UTF7                 = 65000;       // UTF-7 translation
static const int CP_UTF8                 = 65001;       // UTF-8 translation

]]

-- Options for WinHttpSendRequest
WINHTTP_NO_ADDITIONAL_HEADERS = nil
WINHTTP_NO_REQUEST_DATA = nil


-- This will become wide (L"GET" or 2 bytes per character) later in the code, now just simple string
local GET_wstr = "GET"
local UserAgent_wstr = "A WinHTTP from Lua OBS example/1.0"

-- IP and port (PC address in local network)
local ServerName_wstr = [[192.168.0.2]]
local ServerPort = 8080
---------------------------------------------
--
-- Your Command (target object), MODIFY IT!!!
--
local ObjectName_wstr = [[/ExecuteMacro=7f86b4a0-6edd-4415-89c7-199b2854a307/363edea7-ccad-4c40-a877-3c493d37aeed]]

function AnsiToUnicode16L(in_Src, nsrcBytes)
    nsrcBytes = nsrcBytes or #in_Src
    print("nsrcBytes: " .. nsrcBytes)

    -- Find out how many characters needed
    local charsneeded = kernel32.MultiByteToWideChar(kernel32.CP_ACP, 0, in_Src, nsrcBytes, nil, 0);
    print("charsneeded: " .. charsneeded)

    if charsneeded < 0 then
        return nil;
    end
    
    local buff = ffi.new("uint16_t[?]", charsneeded + 1)

    local charswritten = kernel32.MultiByteToWideChar(kernel32.CP_ACP, 0, in_Src, nsrcBytes, buff, charsneeded)
    -- Wide Null terminator
    buff[charswritten] = 0
    print("charswritten: " .. charswritten)

    return buff;
end

function Unicode16ToAnsi(in_Src)
    local srcShorts = ffi.cast("const uint16_t *", in_Src)

    -- Find out how many characters needed
    local bytesneeded = kernel32.WideCharToMultiByte(kernel32.CP_ACP, 0, srcShorts, -1, nil, 0, nil, nil);
    print("bytesneeded: " .. bytesneeded);

    if bytesneeded <= 0 then
        return nil;
    end

    local buff = ffi.new("uint8_t[?]", bytesneeded + 1)
    local byteswritten = kernel32.WideCharToMultiByte(kernel32.CP_ACP, 0, srcShorts, -1, buff, bytesneeded, nil, nil);
    -- Null terminator
    buff[byteswritten] = 0

    print("charswritten: " .. byteswritten)

    return ffi.string(buff, byteswritten - 1);
end

-- Update variables with wide values
UserAgent_wstr = AnsiToUnicode16L(UserAgent_wstr)
ServerName_wstr = AnsiToUnicode16L(ServerName_wstr)
GET_wstr = AnsiToUnicode16L(GET_wstr)
ObjectName_wstr = AnsiToUnicode16L(ObjectName_wstr)

-- This function will send a HTTP Get request
function send_winhttp_request()
    -- No proxy, performed synchronously, no TLS
    local hSession = winhttp.WinHttpOpen(UserAgent_wstr, 1, nil, nil, 0)
    print(hSession)
    if not hSession then
        print("Error. Could not create session " .. ffi.C.GetLastError())
        --return
    end

    -- Specify an HTTP server and port
    local hConnect = winhttp.WinHttpConnect(hSession, ServerName_wstr, ServerPort, 0)
    print(hConnect)
    if not hConnect then
        print("Error. Could not connect to " .. ServerName_wstr .. ", " .. ffi.C.GetLastError())
        --return
    end
    
-- Create an HTTP Request handle (GET, target resource, HTTP/1.1, no referring, default accept types, not secure transaction)
    local hRequest = winhttp.WinHttpOpenRequest(hConnect, GET_wstr, ObjectName_wstr, nil, nil, nil, 0)
    print(hRequest)
    if not hRequest then
        print("Error. Could not create request " .. ffi.C.GetLastError())
        --return
    end

-- Send a request
    local bResults = winhttp.WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)

-- Report errors
    if not bResults then
        print("Error. Error has occurred " .. ffi.C.GetLastError())
    end

--[[ Response
    local response = winhttp.WinHttpReceiveResponse(request, nil)
    print(response)
    if not tonumber(response) then
        print("Error. Could not receive response " .. ffi.C.GetLastError())
        return
    end
    local size = ffi.new("LPDWORD")
    winhttp.WinHttpQueryDataAvailable(request, size)
    print(size)
    if not tonumber(size) or not tonumber(size[0]) then
        print("Error. No data available " .. ffi.C.GetLastError())
        return
    end
]]

-- Close open handles
    if hRequest then winhttp.WinHttpCloseHandle(hRequest) end
    if hConnect then winhttp.WinHttpCloseHandle(hConnect) end
    if hSession then winhttp.WinHttpCloseHandle(hSession) end
    print("Ended")
end

-- Call the send_winhttp_request on event
function on_event(event)
    if event == obs.OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED then
        send_winhttp_request()
    end
end

function script_description()
    return "Example!\n\nSend a HTTP Get request:\n" ..
    Unicode16ToAnsi(ObjectName_wstr) .. " on REPLAY_BUFFER_STARTED"
end

function script_load(settings)
    obs.obs_frontend_add_event_callback(on_event)
end
 

sutex

New Member
As soon as your util supports HTTP requests I think it is better to use HTTP GET request instead of hiding cmd window.
There are number of ways to make it possible.

Example (only tested in ancient W7x64 machine, old OBS, and VoiceMacro 1.4).
"GET" request from Lua script on Windows (uses only libs shipped with OBS and OS):
Lua:
local obs = obslua

local ffi = require("ffi")
local winhttp = ffi.load("winhttp")
local kernel32 = ffi.load("kernel32")

ffi.cdef[[
 typedef void* HINTERNET;
 typedef unsigned int INTERNET_PORT;

 typedef const wchar_t* LPCWSTR;
 typedef unsigned long DWORD;
 typedef unsigned long DWORD_PTR;
 typedef DWORD* LPDWORD;
 typedef void* LPVOID;
 typedef int BOOL;

 
 static const int INTERNET_DEFAULT_PORT          = 0;
 static const int INTERNET_DEFAULT_HTTP_PORT     = 80;
 static const int INTERNET_DEFAULT_HTTPS_PORT    = 443;

 /* flags for WinHttpOpenRequest */
 static const int WINHTTP_FLAG_SECURE            = 0x00800000;

 HINTERNET WinHttpOpen(LPCWSTR pwszUserAgent, DWORD dwAccessType, LPCWSTR pwszProxyName, LPCWSTR pwszProxyBypass, DWORD dwFlags);
 HINTERNET WinHttpConnect(HINTERNET hSession, LPCWSTR pswzServerName, INTERNET_PORT nServerPort, DWORD dwReserved);
 HINTERNET WinHttpOpenRequest(HINTERNET hConnect, LPCWSTR pwszVerb, LPCWSTR pwszObjectName, LPCWSTR pwszVersion, LPCWSTR pwszReferrer, LPCWSTR *ppwszAcceptTypes, DWORD dwFlags);
 BOOL WinHttpSendRequest(HINTERNET hRequest, LPCWSTR lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, DWORD_PTR dwContext);
 BOOL WinHttpReceiveResponse(HINTERNET hRequest, LPVOID lpReserved);
 BOOL WinHttpQueryDataAvailable(HINTERNET hRequest, LPDWORD lpdwNumberOfBytesAvailable);
 BOOL WinHttpCloseHandle(HINTERNET hInternet);
 DWORD GetLastError(void);

 typedef unsigned int UINT;
 typedef const char* LPCSTR;
 typedef wchar_t* LPWSTR;
 typedef char* LPSTR;
 typedef int* LPBOOL;
 
 int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar);
 int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar);
 
/*  Code Page Default Values */
static const int CP_ACP                  = 0;           // default to ANSI code page
static const int CP_OEMCP                = 1;           // default to OEM  code page
static const int CP_MACCP                = 2;           // default to MAC  code page
static const int CP_THREAD_ACP           = 3;           // current thread's ANSI code page
static const int CP_SYMBOL               = 42;          // SYMBOL translations

static const int CP_UTF7                 = 65000;       // UTF-7 translation
static const int CP_UTF8                 = 65001;       // UTF-8 translation

]]

-- Options for WinHttpSendRequest
WINHTTP_NO_ADDITIONAL_HEADERS = nil
WINHTTP_NO_REQUEST_DATA = nil


-- This will become wide (L"GET" or 2 bytes per character) later in the code, now just simple string
local GET_wstr = "GET"
local UserAgent_wstr = "A WinHTTP from Lua OBS example/1.0"

-- IP and port (PC address in local network)
local ServerName_wstr = [[192.168.0.2]]
local ServerPort = 8080
---------------------------------------------
--
-- Your Command (target object), MODIFY IT!!!
--
local ObjectName_wstr = [[/ExecuteMacro=7f86b4a0-6edd-4415-89c7-199b2854a307/363edea7-ccad-4c40-a877-3c493d37aeed]]

function AnsiToUnicode16L(in_Src, nsrcBytes)
    nsrcBytes = nsrcBytes or #in_Src
    print("nsrcBytes: " .. nsrcBytes)

    -- Find out how many characters needed
    local charsneeded = kernel32.MultiByteToWideChar(kernel32.CP_ACP, 0, in_Src, nsrcBytes, nil, 0);
    print("charsneeded: " .. charsneeded)

    if charsneeded < 0 then
        return nil;
    end
 
    local buff = ffi.new("uint16_t[?]", charsneeded + 1)

    local charswritten = kernel32.MultiByteToWideChar(kernel32.CP_ACP, 0, in_Src, nsrcBytes, buff, charsneeded)
    -- Wide Null terminator
    buff[charswritten] = 0
    print("charswritten: " .. charswritten)

    return buff;
end

function Unicode16ToAnsi(in_Src)
    local srcShorts = ffi.cast("const uint16_t *", in_Src)

    -- Find out how many characters needed
    local bytesneeded = kernel32.WideCharToMultiByte(kernel32.CP_ACP, 0, srcShorts, -1, nil, 0, nil, nil);
    print("bytesneeded: " .. bytesneeded);

    if bytesneeded <= 0 then
        return nil;
    end

    local buff = ffi.new("uint8_t[?]", bytesneeded + 1)
    local byteswritten = kernel32.WideCharToMultiByte(kernel32.CP_ACP, 0, srcShorts, -1, buff, bytesneeded, nil, nil);
    -- Null terminator
    buff[byteswritten] = 0

    print("charswritten: " .. byteswritten)

    return ffi.string(buff, byteswritten - 1);
end

-- Update variables with wide values
UserAgent_wstr = AnsiToUnicode16L(UserAgent_wstr)
ServerName_wstr = AnsiToUnicode16L(ServerName_wstr)
GET_wstr = AnsiToUnicode16L(GET_wstr)
ObjectName_wstr = AnsiToUnicode16L(ObjectName_wstr)

-- This function will send a HTTP Get request
function send_winhttp_request()
    -- No proxy, performed synchronously, no TLS
    local hSession = winhttp.WinHttpOpen(UserAgent_wstr, 1, nil, nil, 0)
    print(hSession)
    if not hSession then
        print("Error. Could not create session " .. ffi.C.GetLastError())
        --return
    end

    -- Specify an HTTP server and port
    local hConnect = winhttp.WinHttpConnect(hSession, ServerName_wstr, ServerPort, 0)
    print(hConnect)
    if not hConnect then
        print("Error. Could not connect to " .. ServerName_wstr .. ", " .. ffi.C.GetLastError())
        --return
    end
 
-- Create an HTTP Request handle (GET, target resource, HTTP/1.1, no referring, default accept types, not secure transaction)
    local hRequest = winhttp.WinHttpOpenRequest(hConnect, GET_wstr, ObjectName_wstr, nil, nil, nil, 0)
    print(hRequest)
    if not hRequest then
        print("Error. Could not create request " .. ffi.C.GetLastError())
        --return
    end

-- Send a request
    local bResults = winhttp.WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)

-- Report errors
    if not bResults then
        print("Error. Error has occurred " .. ffi.C.GetLastError())
    end

--[[ Response
    local response = winhttp.WinHttpReceiveResponse(request, nil)
    print(response)
    if not tonumber(response) then
        print("Error. Could not receive response " .. ffi.C.GetLastError())
        return
    end
    local size = ffi.new("LPDWORD")
    winhttp.WinHttpQueryDataAvailable(request, size)
    print(size)
    if not tonumber(size) or not tonumber(size[0]) then
        print("Error. No data available " .. ffi.C.GetLastError())
        return
    end
]]

-- Close open handles
    if hRequest then winhttp.WinHttpCloseHandle(hRequest) end
    if hConnect then winhttp.WinHttpCloseHandle(hConnect) end
    if hSession then winhttp.WinHttpCloseHandle(hSession) end
    print("Ended")
end

-- Call the send_winhttp_request on event
function on_event(event)
    if event == obs.OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED then
        send_winhttp_request()
    end
end

function script_description()
    return "Example!\n\nSend a HTTP Get request:\n" ..
    Unicode16ToAnsi(ObjectName_wstr) .. " on REPLAY_BUFFER_STARTED"
end

function script_load(settings)
    obs.obs_frontend_add_event_callback(on_event)

[/QUOTE]
That works; no window popup; perfect; not sure what you meant by this. As soon as your util supports HTTP requests, I think it is better to use HTTP GET, as I had no issues changing the ip and the /ExecuteMacro= to get the script working.

Now I have something to work with, thanks for your help! :)
 
Top