LunaTranslator/cpp/exec/PyStand.cpp

355 lines
9.0 KiB
C++
Raw Normal View History

2024-08-07 14:14:04 +08:00

2024-05-01 14:59:50 +08:00
#include "PyStand.h"
2024-10-09 00:09:15 +08:00
#include <iomanip>
#include <sstream>
#include <chrono>
#include <ctime>
2024-05-01 14:59:50 +08:00
//---------------------------------------------------------------------
// dtor
//---------------------------------------------------------------------
PyStand::~PyStand()
{
FreeLibrary(_hDLL);
}
//---------------------------------------------------------------------
// ctor
//---------------------------------------------------------------------
PyStand::PyStand(const wchar_t *runtime)
{
_hDLL = NULL;
_Py_Main = NULL;
2024-05-01 17:28:43 +08:00
if (CheckEnviron(runtime) == false)
{
2024-05-01 14:59:50 +08:00
exit(1);
}
2024-05-01 17:28:43 +08:00
if (LoadPython() == false)
{
2024-05-01 14:59:50 +08:00
exit(2);
}
}
//---------------------------------------------------------------------
2024-05-01 17:28:43 +08:00
// init: _args, _argv, _cwd, _pystand, _home, _runtime,
2024-05-01 14:59:50 +08:00
//---------------------------------------------------------------------
bool PyStand::CheckEnviron(const wchar_t *rtp)
{
// init: _args, _argv
LPWSTR *argvw;
int argc;
_args = GetCommandLineW();
argvw = CommandLineToArgvW(_args.c_str(), &argc);
2024-05-01 17:28:43 +08:00
if (argvw == NULL)
{
2024-05-01 14:59:50 +08:00
MessageBoxA(NULL, "Error in CommandLineToArgvW()", "ERROR", MB_OK);
return false;
}
_argv.resize(argc);
2024-05-01 17:28:43 +08:00
for (int i = 0; i < argc; i++)
{
2024-05-01 14:59:50 +08:00
_argv[i] = argvw[i];
}
LocalFree(argvw);
wchar_t path[MAX_PATH + 10];
// init: _pystand (full path of PyStand.exe)
GetModuleFileNameW(NULL, path, MAX_PATH + 1);
#if 0
wsprintf(path, L"e:\\github\\tools\\pystand\\pystand.exe");
#endif
_pystand = path;
2024-11-04 00:53:21 +08:00
_home = std::filesystem::path(path).parent_path().wstring();
2024-05-01 14:59:50 +08:00
2024-11-04 00:53:21 +08:00
SetCurrentDirectoryW(_home.c_str());
_runtime = (std::filesystem::path(_home) / rtp).wstring();
2024-05-01 14:59:50 +08:00
// check home
2024-11-04 00:53:21 +08:00
if (!PathFileExistsW(_runtime.c_str()))
2024-05-01 17:28:43 +08:00
{
2024-11-04 00:53:21 +08:00
std::wstring msg = L"Missing embedded Python3 in:\n" + _runtime;
2024-05-01 14:59:50 +08:00
MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK);
return false;
}
2024-11-15 04:03:18 +08:00
#ifndef WINXP
2024-05-01 14:59:50 +08:00
// check python3.dll
2024-11-04 00:53:21 +08:00
if (!PathFileExistsW((_runtime + L"\\python3.dll").c_str()))
2024-05-01 17:28:43 +08:00
{
2024-11-04 00:53:21 +08:00
std::wstring msg = L"Missing python3.dll in:\r\n" + _runtime;
2024-05-01 14:59:50 +08:00
MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK);
return false;
}
2024-11-15 04:03:18 +08:00
#else
if (!PathFileExistsW((_runtime + L"\\python34.dll").c_str()))
{
std::wstring msg = L"Missing python34.dll in:\r\n" + _runtime;
MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK);
return false;
}
#endif
2024-05-01 14:59:50 +08:00
// setup environment
SetEnvironmentVariableW(L"PYSTAND", _pystand.c_str());
SetEnvironmentVariableW(L"PYSTAND_HOME", _home.c_str());
SetEnvironmentVariableW(L"PYSTAND_RUNTIME", _runtime.c_str());
// unnecessary to init PYSTAND_SCRIPT here.
#if 0
SetEnvironmentVariableW(L"PYSTAND_SCRIPT", _script.c_str());
#endif
#if 0
wprintf(L"%s - %s\n", _pystand.c_str(), path);
MessageBoxW(NULL, _pystand.c_str(), _home.c_str(), MB_OK);
#endif
return true;
}
//---------------------------------------------------------------------
// load python
//---------------------------------------------------------------------
bool PyStand::LoadPython()
{
std::wstring runtime = _runtime;
std::wstring previous;
// save current directory
wchar_t path[MAX_PATH + 10];
GetCurrentDirectoryW(MAX_PATH + 1, path);
previous = path;
// python dll must be load under "runtime"
SetCurrentDirectoryW(runtime.c_str());
// LoadLibrary
2024-11-15 04:03:18 +08:00
#ifndef WINXP
2024-05-01 14:59:50 +08:00
_hDLL = (HINSTANCE)LoadLibraryA("python3.dll");
2024-11-15 04:03:18 +08:00
#else
_hDLL = (HINSTANCE)LoadLibraryA("python34.dll");
#endif
2024-05-01 17:28:43 +08:00
if (_hDLL)
{
2024-05-01 14:59:50 +08:00
_Py_Main = (t_Py_Main)GetProcAddress(_hDLL, "Py_Main");
2024-05-01 17:28:43 +08:00
}
2024-05-01 14:59:50 +08:00
// restore director
SetCurrentDirectoryW(previous.c_str());
2024-05-01 17:28:43 +08:00
if (_hDLL == NULL)
{
2024-05-01 14:59:50 +08:00
std::wstring msg = L"Cannot load python3.dll from:\r\n" + runtime;
MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK);
return false;
}
2024-05-01 17:28:43 +08:00
else if (_Py_Main == NULL)
{
2024-05-01 14:59:50 +08:00
std::wstring msg = L"Cannot find Py_Main() in:\r\n";
msg += runtime + L"\\python3.dll";
MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK);
return false;
}
return true;
}
//---------------------------------------------------------------------
// run string
//---------------------------------------------------------------------
int PyStand::RunString(const wchar_t *script)
{
2024-05-01 17:28:43 +08:00
if (_Py_Main == NULL)
{
2024-05-01 14:59:50 +08:00
return -1;
}
int hr = 0;
int i;
_py_argv.resize(0);
// init arguments
_py_argv.push_back(_argv[0]);
_py_argv.push_back(L"-I");
_py_argv.push_back(L"-s");
_py_argv.push_back(L"-S");
_py_argv.push_back(L"-c");
_py_argv.push_back(script);
2024-05-01 17:28:43 +08:00
for (i = 1; i < (int)_argv.size(); i++)
{
2024-05-01 14:59:50 +08:00
_py_argv.push_back(_argv[i]);
}
// finalize arguments
_py_args.resize(0);
2024-05-01 17:28:43 +08:00
for (i = 0; i < (int)_py_argv.size(); i++)
{
_py_args.push_back((wchar_t *)_py_argv[i].c_str());
2024-05-01 14:59:50 +08:00
}
2024-11-15 04:48:32 +08:00
#ifdef WINXP
auto Py_SetPath = (void (*)(const wchar_t *))GetProcAddress(_hDLL, "Py_SetPath");
Py_SetPath(L"./files/runtime/Lib;./files/runtime/DLLs;./files/runtime/Lib/site-packages");
#endif
2024-05-01 14:59:50 +08:00
hr = _Py_Main((int)_py_args.size(), &_py_args[0]);
return hr;
}
//---------------------------------------------------------------------
// LoadScript()
//---------------------------------------------------------------------
int PyStand::DetectScript()
{
// init: _script (init script like PyStand.int or PyStand.py)
int size = (int)_pystand.size() - 1;
2024-05-01 17:28:43 +08:00
for (; size >= 0; size--)
{
if (_pystand[size] == L'.')
break;
2024-05-01 14:59:50 +08:00
}
2024-05-01 17:28:43 +08:00
if (size < 0)
size = (int)_pystand.size();
2024-05-01 14:59:50 +08:00
std::wstring main = _pystand.substr(0, size);
2024-05-01 17:28:43 +08:00
std::vector<const wchar_t *> exts;
2024-05-01 14:59:50 +08:00
std::vector<std::wstring> scripts;
_script.clear();
2024-05-01 17:12:05 +08:00
2024-05-01 14:59:50 +08:00
std::wstring test;
2024-05-01 17:28:43 +08:00
test = _home + L"\\LunaTranslator\\LunaTranslator_main.py";
if (PathFileExistsW(test.c_str()))
{
2024-05-01 14:59:50 +08:00
_script = test;
}
2024-05-01 17:28:43 +08:00
if (_script.empty())
{
std::wstring msg = L"Can't find :\r\n" + test;
MessageBoxW(NULL, msg.c_str(), L"ERROR", MB_OK);
return -1;
2024-05-01 14:59:50 +08:00
}
SetEnvironmentVariableW(L"PYSTAND_SCRIPT", _script.c_str());
2024-07-18 16:34:41 +08:00
2024-05-01 19:34:32 +08:00
std::vector<wchar_t> buffer(MAX_PATH);
GetModuleFileNameW(GetModuleHandle(0), buffer.data(), MAX_PATH);
SetEnvironmentVariableW(L"LUNA_EXE_NAME", buffer.data());
2024-07-18 16:34:41 +08:00
2024-05-01 14:59:50 +08:00
return 0;
}
//---------------------------------------------------------------------
// init script
//---------------------------------------------------------------------
2024-05-01 17:28:43 +08:00
const auto init_script =
2024-10-09 00:09:15 +08:00
LR"(
2024-12-24 15:22:04 +08:00
import os,functools, locale, sys
2024-10-09 00:09:15 +08:00
PYSTAND = os.environ['PYSTAND']
PYSTAND_HOME = os.environ['PYSTAND_HOME']
PYSTAND_RUNTIME = os.environ['PYSTAND_RUNTIME']
PYSTAND_SCRIPT = os.environ['PYSTAND_SCRIPT']
sys.path_origin = [n for n in sys.path]
sys.PYSTAND = PYSTAND
sys.PYSTAND_HOME = PYSTAND_HOME
sys.PYSTAND_SCRIPT = PYSTAND_SCRIPT
def MessageBox(msg, info = 'Message'):
import ctypes
ctypes.windll.user32.MessageBoxW(None, str(msg), str(info), 0)
return 0
os.MessageBox = MessageBox
2024-10-30 15:32:11 +08:00
#sys.stdout=sys.stderr
2024-10-29 20:24:28 +08:00
sys.path.insert(0, './LunaTranslator')
2024-12-24 15:22:04 +08:00
def fuckwrite(origin, message):
try:
if isinstance(message, str):
code=locale.getpreferredencoding()
origin(message.encode(encoding=code, errors='replace').decode(encoding=code, errors='replace'))
else:
origin(message)
except:
return
import traceback, io
sio = io.StringIO()
traceback.print_exc(file = sio)
os.MessageBox(sio.getvalue(), message)
2024-10-09 00:09:15 +08:00
try:
fd = os.open('CONOUT$', os.O_RDWR | os.O_BINARY)
fp = os.fdopen(fd, 'w')
sys.stdout = fp
sys.stderr = fp
attached = True
2024-12-24 15:22:04 +08:00
sys.stdout.write = functools.partial(fuckwrite,sys.stdout.write)
sys.stderr.write = functools.partial(fuckwrite,sys.stderr.write)
2024-10-09 00:09:15 +08:00
except Exception as e:
2024-10-29 15:53:11 +08:00
try:
2024-12-24 15:22:04 +08:00
fp = open(os.devnull, 'w', errors='replace') # sometimes FileNotFound Error: [Errno 2]No such file or directory: 'nul'
2024-10-29 15:53:11 +08:00
sys.stdout = fp
sys.stderr = fp
attached = False
except:
pass
2024-12-24 15:22:04 +08:00
2024-10-09 00:09:15 +08:00
sys.argv = [PYSTAND_SCRIPT] + sys.argv[1:]
text = open(PYSTAND_SCRIPT, 'rb').read()
environ = {'__file__': PYSTAND_SCRIPT, '__name__': '__main__'}
environ['__package__'] = None
)"
2024-05-01 14:59:50 +08:00
#ifndef PYSTAND_CONSOLE
2024-10-09 00:09:15 +08:00
LR"(
try:
code = compile(text, PYSTAND_SCRIPT, 'exec')
exec(code, environ)
except Exception:
if attached:
raise
import traceback, io
sio = io.StringIO()
traceback.print_exc(file = sio)
os.MessageBox(sio.getvalue(), 'Error')
)"
2024-05-01 14:59:50 +08:00
#else
2024-10-09 00:09:15 +08:00
LR"(
code = compile(text, PYSTAND_SCRIPT, 'exec')
exec(code, environ)
)"
2024-05-01 14:59:50 +08:00
#endif
2024-05-01 17:28:43 +08:00
"";
2024-05-01 14:59:50 +08:00
//---------------------------------------------------------------------
// main
//---------------------------------------------------------------------
//! flag: -static
2024-05-01 17:28:43 +08:00
//! src:
2024-05-01 14:59:50 +08:00
//! link: stdc++, shlwapi, resource.o
//! prebuild: windres resource.rc -o resource.o
//! mode: win
//! int: objs
int main()
{
2024-08-07 14:14:04 +08:00
{
// 当更新进行时,禁止启动
AutoHandle hMutex = CreateMutex(NULL, FALSE, L"LUNA_UPDATER_SINGLE");
if (GetLastError() == ERROR_ALREADY_EXISTS)
return 0;
}
2024-07-18 16:34:41 +08:00
auto __handle = AutoHandle(CreateMutexA(&allAccess, FALSE, "LUNA_UPDATER_BLOCK"));
2024-11-02 19:55:32 +08:00
PyStand ps(L"files\\runtime");
2024-05-01 17:28:43 +08:00
if (ps.DetectScript() != 0)
{
2024-05-01 14:59:50 +08:00
return 3;
}
2024-12-24 15:22:04 +08:00
// print cmd无法显示的字符时如果使用cmd打开不论debug还是普通都会error31崩溃。如果双击打开debug却不会崩溃
// 但因为无法区分是使用cmd打开debug还是双击打开debug所以干脆都这样吧。
2024-05-01 17:28:43 +08:00
if (AttachConsole(ATTACH_PARENT_PROCESS))
{
2024-05-01 14:59:50 +08:00
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
}
int hr = ps.RunString(init_script);
return hr;
}
2024-10-09 00:09:15 +08:00
int WINAPI
WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int show)
{
return main();
}