mirror of
https://github.com/pjlt/lanthing-pc.git
synced 2026-05-07 06:27:34 +08:00
1. 删除宏UNICODE和_UNICODE
2. 修复app.exe和worker析构顺序不对导致的卡死 3. 修改CMakeLists.txt的install部分
This commit is contained in:
@@ -31,7 +31,7 @@ endif()
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(LT_WINDOWS)
|
||||
add_compile_definitions(UNICODE _UNICODE WIN32 _WIN32 NOMINMAX WIN32_LEAN_AND_MEAN)# NOGDI)
|
||||
add_compile_definitions(WIN32 _WIN32 NOMINMAX WIN32_LEAN_AND_MEAN)# NOGDI)
|
||||
add_compile_options(/wd4819 /utf-8)
|
||||
#add_compile_options(/W4 /WX)
|
||||
else()
|
||||
|
||||
@@ -53,7 +53,10 @@ App::App() {
|
||||
}
|
||||
|
||||
App::~App() {
|
||||
//
|
||||
if (ioloop_) {
|
||||
ioloop_->stop();
|
||||
}
|
||||
thread_.reset();
|
||||
}
|
||||
|
||||
bool App::init() {
|
||||
|
||||
@@ -134,6 +134,22 @@ target_link_libraries(${PROJECT_NAME}
|
||||
|
||||
install(TARGETS ${PROJECT_NAME})
|
||||
|
||||
install(CODE [[
|
||||
file(GET_RUNTIME_DEPENDENCIES
|
||||
RESOLVED_DEPENDENCIES_VAR RESOLVED_DEPS
|
||||
UNRESOLVED_DEPENDENCIES_VAR UNRESOLVED_DEPS
|
||||
EXECUTABLES $<TARGET_FILE:lanthing>
|
||||
DIRECTORIES ${CMAKE_SOURCE_DIR}
|
||||
PRE_INCLUDE_REGEXES ${CMAKE_SOURCE_DIR}
|
||||
POST_INCLUDE_REGEXES ${CMAKE_SOURCE_DIR}
|
||||
PRE_EXCLUDE_REGEXES "system32"
|
||||
POST_EXCLUDE_REGEXES "system32"
|
||||
)
|
||||
foreach(DEP_LIB ${RESOLVED_DEPS})
|
||||
file(INSTALL ${DEP_LIB} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
endforeach()
|
||||
]])
|
||||
|
||||
deploy_dlls(${PROJECT_NAME})
|
||||
|
||||
# 设置VS调试路径
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
#include "worker_process.h"
|
||||
#include <Windows.h>
|
||||
#include <g3log/g3log.hpp>
|
||||
#include <ltproto/peer2peer/stop_working.pb.h>
|
||||
#include <ltlib/system.h>
|
||||
#include <ltlib/strings.h>
|
||||
#include <ltlib/system.h>
|
||||
#include <ltproto/peer2peer/stop_working.pb.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace {
|
||||
|
||||
std::string to_string(std::vector<rtc::VideoCodecType> codecs)
|
||||
{
|
||||
std::string to_string(std::vector<rtc::VideoCodecType> codecs) {
|
||||
std::string str;
|
||||
for (size_t i = 0; i < codecs.size(); i++) {
|
||||
switch (codecs[i]) {
|
||||
@@ -32,38 +30,30 @@ std::string to_string(std::vector<rtc::VideoCodecType> codecs)
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace lt
|
||||
{
|
||||
namespace lt {
|
||||
|
||||
namespace svc
|
||||
{
|
||||
namespace svc {
|
||||
|
||||
std::unique_ptr<WorkerProcess> WorkerProcess::create(const Params& params)
|
||||
{
|
||||
std::unique_ptr<WorkerProcess> process { new WorkerProcess(params) };
|
||||
std::unique_ptr<WorkerProcess> WorkerProcess::create(const Params& params) {
|
||||
std::unique_ptr<WorkerProcess> process{new WorkerProcess(params)};
|
||||
process->start();
|
||||
return process;
|
||||
}
|
||||
|
||||
WorkerProcess::WorkerProcess(const Params& params)
|
||||
: on_stoped_ { params.on_stoped }
|
||||
, path_ { params.path }
|
||||
, pipe_name_ { params.pipe_name }
|
||||
, client_width_ { params.client_width }
|
||||
, client_height_ { params.client_height }
|
||||
, client_refresh_rate_ { params.client_refresh_rate }
|
||||
, client_codecs_ { params.client_codecs }
|
||||
, run_as_win_service_ { ltlib::is_run_as_service() }
|
||||
{
|
||||
}
|
||||
: on_stoped_{params.on_stoped}
|
||||
, path_{params.path}
|
||||
, pipe_name_{params.pipe_name}
|
||||
, client_width_{params.client_width}
|
||||
, client_height_{params.client_height}
|
||||
, client_refresh_rate_{params.client_refresh_rate}
|
||||
, client_codecs_{params.client_codecs}
|
||||
, run_as_win_service_{ltlib::is_run_as_service()} {}
|
||||
|
||||
WorkerProcess::~WorkerProcess()
|
||||
{
|
||||
}
|
||||
WorkerProcess::~WorkerProcess() {}
|
||||
|
||||
void WorkerProcess::start()
|
||||
{
|
||||
std::lock_guard lk { mutex_ };
|
||||
void WorkerProcess::start() {
|
||||
std::lock_guard lk{mutex_};
|
||||
if (thread_ != nullptr) {
|
||||
LOG(WARNING) << "Host process already launched";
|
||||
return;
|
||||
@@ -72,20 +62,21 @@ void WorkerProcess::start()
|
||||
std::promise<void> promise;
|
||||
auto future = promise.get_future();
|
||||
thread_ = ltlib::BlockingThread::create(
|
||||
"worker_process", [this, &promise](const std::function<void()>& i_am_alive, void*) {
|
||||
"worker_process",
|
||||
[this, &promise](const std::function<void()>& i_am_alive, void*) {
|
||||
main_loop(promise, i_am_alive);
|
||||
},
|
||||
nullptr);
|
||||
future.get();
|
||||
}
|
||||
|
||||
void WorkerProcess::main_loop(std::promise<void>& promise, const std::function<void()>& i_am_alive)
|
||||
{
|
||||
void WorkerProcess::main_loop(std::promise<void>& promise,
|
||||
const std::function<void()>& i_am_alive) {
|
||||
while (!stoped_) {
|
||||
i_am_alive();
|
||||
if (!launch_worker_process()) {
|
||||
LOG(WARNING) << "Launch worker process failed";
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds { 100 });
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{100});
|
||||
continue;
|
||||
}
|
||||
promise.set_value();
|
||||
@@ -93,16 +84,11 @@ void WorkerProcess::main_loop(std::promise<void>& promise, const std::function<v
|
||||
}
|
||||
}
|
||||
|
||||
bool WorkerProcess::launch_worker_process()
|
||||
{
|
||||
bool WorkerProcess::launch_worker_process() {
|
||||
std::stringstream ss;
|
||||
ss << path_
|
||||
<< " -type worker "
|
||||
<< " -name " << pipe_name_
|
||||
<< " -width " << client_width_
|
||||
<< " -height " << client_height_
|
||||
<< " -freq " << client_refresh_rate_
|
||||
<< " -codecs " << ::to_string(client_codecs_);
|
||||
ss << path_ << " -type worker "
|
||||
<< " -name " << pipe_name_ << " -width " << client_width_ << " -height " << client_height_
|
||||
<< " -freq " << client_refresh_rate_ << " -codecs " << ::to_string(client_codecs_);
|
||||
std::wstring cmd = ltlib::utf8_to_utf16(ss.str());
|
||||
if (process_handle_) {
|
||||
CloseHandle(process_handle_);
|
||||
@@ -123,7 +109,8 @@ bool WorkerProcess::launch_worker_process()
|
||||
LOG(WARNING) << "OpenProcessToken fail: " << GetLastError();
|
||||
return ret;
|
||||
}
|
||||
if (!DuplicateTokenEx(token, MAXIMUM_ALLOWED, 0, SecurityImpersonation, TokenPrimary, &user_token)) {
|
||||
if (!DuplicateTokenEx(token, MAXIMUM_ALLOWED, 0, SecurityImpersonation, TokenPrimary,
|
||||
&user_token)) {
|
||||
LOG(WARNING) << "DuplicateTokenEx fail: " << GetLastError();
|
||||
return ret;
|
||||
}
|
||||
@@ -133,25 +120,27 @@ bool WorkerProcess::launch_worker_process()
|
||||
LOG(WARNING) << "WTSGetActiveConsoleSessionId fail" << GetLastError();
|
||||
return ret;
|
||||
}
|
||||
if (!SetTokenInformation(user_token, (TOKEN_INFORMATION_CLASS)TokenSessionId, &curr_session_id, sizeof(curr_session_id))) {
|
||||
if (!SetTokenInformation(user_token, (TOKEN_INFORMATION_CLASS)TokenSessionId,
|
||||
&curr_session_id, sizeof(curr_session_id))) {
|
||||
LOG(WARNING) << "SetTokenInformation fail: " << GetLastError();
|
||||
return ret;
|
||||
}
|
||||
DWORD ui_access = 1;
|
||||
if (!SetTokenInformation(user_token, (TOKEN_INFORMATION_CLASS)TokenUIAccess, &ui_access, sizeof(ui_access))) {
|
||||
if (!SetTokenInformation(user_token, (TOKEN_INFORMATION_CLASS)TokenUIAccess, &ui_access,
|
||||
sizeof(ui_access))) {
|
||||
LOG(WARNING) << "SetTokenInformation fail: " << GetLastError();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
PROCESS_INFORMATION pi = { 0 };
|
||||
SECURITY_ATTRIBUTES sa = { 0 };
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
SECURITY_ATTRIBUTES sa = {0};
|
||||
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
STARTUPINFO si = { 0 };
|
||||
STARTUPINFOW si = {0};
|
||||
si.dwFlags = STARTF_USESHOWWINDOW;
|
||||
si.cb = sizeof(STARTUPINFO);
|
||||
si.wShowWindow = SW_SHOW;
|
||||
ret = CreateProcessAsUserW(user_token, NULL, const_cast<LPTSTR>(cmd.c_str()),
|
||||
&sa, &sa, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
|
||||
ret = CreateProcessAsUserW(user_token, NULL, const_cast<LPWSTR>(cmd.c_str()), &sa, &sa, FALSE,
|
||||
NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
|
||||
CloseHandle(token);
|
||||
CloseHandle(user_token);
|
||||
if (!ret) {
|
||||
@@ -164,8 +153,7 @@ bool WorkerProcess::launch_worker_process()
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WorkerProcess::wait_for_worker_process(const std::function<void()>& i_am_alive)
|
||||
{
|
||||
void WorkerProcess::wait_for_worker_process(const std::function<void()>& i_am_alive) {
|
||||
int32_t count_seconds = 0;
|
||||
while (!stoped_) {
|
||||
i_am_alive();
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
#include <Windows.h>
|
||||
#include <set>
|
||||
#include <g3log/g3log.hpp>
|
||||
|
||||
#include "display_setting.h"
|
||||
|
||||
namespace lt
|
||||
{
|
||||
#include <g3log/g3log.hpp>
|
||||
#include <set>
|
||||
|
||||
namespace worker
|
||||
{
|
||||
namespace lt {
|
||||
|
||||
DisplaySetting DisplaySettingNegotiator::negotiate(DisplaySetting client_display_setting)
|
||||
{
|
||||
DEVMODE current_mode {};
|
||||
current_mode.dmSize = sizeof(DEVMODE);
|
||||
namespace worker {
|
||||
|
||||
DisplaySetting DisplaySettingNegotiator::negotiate(DisplaySetting client_display_setting) {
|
||||
DEVMODEW current_mode{};
|
||||
current_mode.dmSize = sizeof(DEVMODEW);
|
||||
if (EnumDisplaySettingsW(nullptr, ENUM_CURRENT_SETTINGS, ¤t_mode) == 0) {
|
||||
LOG(WARNING) << "Enumerate current display settings failed";
|
||||
return {};
|
||||
}
|
||||
|
||||
// 比较{width, height, refresh_rate ± 1}
|
||||
std::set<DisplaySetting, decltype(&DisplaySetting::compare_full_loose)> available_settings { &DisplaySetting::compare_full_loose };
|
||||
DEVMODE mode {};
|
||||
mode.dmSize = sizeof(DEVMODE);
|
||||
std::set<DisplaySetting, decltype(&DisplaySetting::compare_full_loose)> available_settings{
|
||||
&DisplaySetting::compare_full_loose};
|
||||
DEVMODEW mode{};
|
||||
mode.dmSize = sizeof(DEVMODEW);
|
||||
DWORD mode_num = 0;
|
||||
while (EnumDisplaySettingsW(nullptr, mode_num, &mode) != 0) {
|
||||
mode_num += 1;
|
||||
@@ -34,7 +34,9 @@ DisplaySetting DisplaySettingNegotiator::negotiate(DisplaySetting client_display
|
||||
}
|
||||
|
||||
// 比较{width, height}
|
||||
std::set<DisplaySetting, decltype(&DisplaySetting::compare_width_height)> avaiable_settings2 { available_settings.begin(), available_settings.end(), &DisplaySetting::compare_width_height };
|
||||
std::set<DisplaySetting, decltype(&DisplaySetting::compare_width_height)> avaiable_settings2{
|
||||
available_settings.begin(), available_settings.end(),
|
||||
&DisplaySetting::compare_width_height};
|
||||
auto iter2 = avaiable_settings2.find(client_display_setting);
|
||||
if (iter2 != avaiable_settings2.end()) {
|
||||
DisplaySetting result = *iter2;
|
||||
@@ -53,12 +55,12 @@ DisplaySetting DisplaySettingNegotiator::negotiate(DisplaySetting client_display
|
||||
}
|
||||
}
|
||||
// 找不到,直接返回host当前的DisplaySetting
|
||||
DisplaySetting result(current_mode.dmPelsWidth, current_mode.dmPelsHeight, current_mode.dmDisplayFrequency);
|
||||
DisplaySetting result(current_mode.dmPelsWidth, current_mode.dmPelsHeight,
|
||||
current_mode.dmDisplayFrequency);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DisplaySetting::compare_full_strict(const DisplaySetting& lhs, const DisplaySetting& rhs)
|
||||
{
|
||||
bool DisplaySetting::compare_full_strict(const DisplaySetting& lhs, const DisplaySetting& rhs) {
|
||||
if (lhs.width != rhs.width) {
|
||||
return lhs.width < rhs.width;
|
||||
}
|
||||
@@ -68,8 +70,7 @@ bool DisplaySetting::compare_full_strict(const DisplaySetting& lhs, const Displa
|
||||
return lhs.refrash_rate < rhs.refrash_rate;
|
||||
}
|
||||
|
||||
bool DisplaySetting::compare_full_loose(const DisplaySetting& lhs, const DisplaySetting& rhs)
|
||||
{
|
||||
bool DisplaySetting::compare_full_loose(const DisplaySetting& lhs, const DisplaySetting& rhs) {
|
||||
if (lhs.width != rhs.width) {
|
||||
return lhs.width < rhs.width;
|
||||
}
|
||||
@@ -82,8 +83,7 @@ bool DisplaySetting::compare_full_loose(const DisplaySetting& lhs, const Display
|
||||
return (rhs.refrash_rate > lhs.refrash_rate) && (rhs.refrash_rate - lhs.refrash_rate > 2);
|
||||
}
|
||||
|
||||
bool DisplaySetting::compare_width_height(const DisplaySetting& lhs, const DisplaySetting& rhs)
|
||||
{
|
||||
bool DisplaySetting::compare_width_height(const DisplaySetting& lhs, const DisplaySetting& rhs) {
|
||||
if (lhs.width != rhs.width) {
|
||||
return lhs.width < rhs.width;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,10 @@ Worker::Worker(const Params& params)
|
||||
, last_time_received_from_service_{ltlib::steady_now_ms()} {}
|
||||
|
||||
Worker::~Worker() {
|
||||
//
|
||||
if (ioloop_) {
|
||||
ioloop_->stop();
|
||||
}
|
||||
thread_.reset();
|
||||
}
|
||||
|
||||
void Worker::wait() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#include <process.h>
|
||||
// #include <process.h>
|
||||
#include <Windows.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <Shlobj.h>
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
BOOL GetTokenByName(HANDLE& hToken, const LPTSTR lpName)
|
||||
BOOL GetTokenByName(HANDLE& hToken, const LPWSTR lpName)
|
||||
{
|
||||
if (!lpName)
|
||||
return FALSE;
|
||||
@@ -26,7 +26,7 @@ BOOL GetTokenByName(HANDLE& hToken, const LPTSTR lpName)
|
||||
|
||||
pe32.dwSize = sizeof(PROCESSENTRY32W);
|
||||
|
||||
if (Process32First(hProcessSnap, &pe32)) {
|
||||
if (Process32FirstW(hProcessSnap, &pe32)) {
|
||||
do {
|
||||
if (!wcscmp(wcsupr(pe32.szExeFile), wcsupr(lpName))) {
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
|
||||
@@ -50,33 +50,18 @@ bool execute_as_user(const std::function<bool(HANDLE)>& func)
|
||||
HANDLE hToken = NULL;
|
||||
bool res = false;
|
||||
do {
|
||||
//可能有些电脑在刚开机一直会失败
|
||||
if (!GetTokenByName(hToken, (const LPTSTR) L"EXPLORER.EXE")) {
|
||||
if (!GetTokenByName(hToken, (const LPWSTR)L"EXPLORER.EXE")) {
|
||||
return false;
|
||||
}
|
||||
////可能进程还没起来
|
||||
//for (int i = 0; i < 1; i++)
|
||||
//{
|
||||
// if (!GetTokenByName(hToken, (const LPTSTR)_T("EXPLORER.EXE")))
|
||||
// {
|
||||
// std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
// continue;
|
||||
// }
|
||||
// break;
|
||||
//}
|
||||
|
||||
if (!hToken) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 模拟登录用户的安全上下文
|
||||
if (FALSE == ImpersonateLoggedOnUser(hToken)) {
|
||||
break;
|
||||
}
|
||||
|
||||
res = func(hToken);
|
||||
|
||||
// 到这里已经模拟完了,别忘记返回原来的安全上下文
|
||||
if (FALSE == RevertToSelf()) {
|
||||
break;
|
||||
}
|
||||
@@ -88,7 +73,7 @@ bool execute_as_user(const std::function<bool(HANDLE)>& func)
|
||||
return res;
|
||||
}
|
||||
|
||||
} // 匿名空间
|
||||
} // namespace
|
||||
|
||||
namespace ltlib
|
||||
{
|
||||
@@ -142,7 +127,7 @@ bool get_program_path(std::wstring& path)
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
template <>
|
||||
std::string get_program_path<char>()
|
||||
{
|
||||
std::string path;
|
||||
@@ -178,7 +163,7 @@ uint32_t get_session_id_by_pid(uint32_t pid)
|
||||
{
|
||||
DWORD sid = 0;
|
||||
if (FALSE == ProcessIdToSessionId(pid, &sid)) {
|
||||
//TODO error handling
|
||||
// TODO error handling
|
||||
return 0;
|
||||
}
|
||||
return sid;
|
||||
@@ -191,7 +176,7 @@ uint32_t get_parent_pid(uint32_t curr_pid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
PROCESSENTRY32 pe32;
|
||||
PROCESSENTRY32W pe32;
|
||||
pe32.dwSize = sizeof(pe32);
|
||||
pe32.dwFlags = sizeof(pe32);
|
||||
BOOL hProcess = Process32FirstW(PHANDLE, &pe32);
|
||||
@@ -225,18 +210,18 @@ std::string get_appdata_path(bool is_service)
|
||||
if (hr != S_OK) {
|
||||
return false;
|
||||
}
|
||||
if (!pidl || !SHGetPathFromIDList(pidl, szDocument)) {
|
||||
if (!pidl || !SHGetPathFromIDListW(pidl, szDocument)) {
|
||||
return false;
|
||||
}
|
||||
CoTaskMemFree(pidl);
|
||||
GetShortPathName(szDocument, m_lpszDefaultDir, _MAX_PATH);
|
||||
appdata_path = utf16_to_utf8(std::wstring(m_lpszDefaultDir));
|
||||
GetShortPathNameW(szDocument, m_lpszDefaultDir, _MAX_PATH);
|
||||
appdata_path = utf16_to_utf8(std::wstring(m_lpszDefaultDir));
|
||||
return true;
|
||||
};
|
||||
|
||||
if (is_service) {
|
||||
if (!execute_as_user(get_path)) {
|
||||
//log_print(kError, _T("get_path fail"));
|
||||
// log_print(kError, _T("get_path fail"));
|
||||
return "";
|
||||
}
|
||||
return appdata_path;
|
||||
@@ -306,13 +291,13 @@ bool set_thread_desktop()
|
||||
break;
|
||||
}
|
||||
|
||||
TCHAR cur_thread_desktop_name[1024] = { 0 };
|
||||
TCHAR cur_input_desktop_name[1024] = { 0 };
|
||||
WCHAR cur_thread_desktop_name[1024] = { 0 };
|
||||
WCHAR cur_input_desktop_name[1024] = { 0 };
|
||||
DWORD needLength = 0;
|
||||
if (!GetUserObjectInformation(thread_desktop, UOI_NAME, cur_thread_desktop_name, sizeof(cur_thread_desktop_name), &needLength)) {
|
||||
if (!GetUserObjectInformationW(thread_desktop, UOI_NAME, cur_thread_desktop_name, sizeof(cur_thread_desktop_name), &needLength)) {
|
||||
break;
|
||||
}
|
||||
if (!GetUserObjectInformation(input_desktop, UOI_NAME, cur_input_desktop_name, sizeof(cur_input_desktop_name), &needLength)) {
|
||||
if (!GetUserObjectInformationW(input_desktop, UOI_NAME, cur_input_desktop_name, sizeof(cur_input_desktop_name), &needLength)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ bool get_service_status(SC_HANDLE service_handle, SERVICE_STATUS_PROCESS& servic
|
||||
return false;
|
||||
}
|
||||
|
||||
} // 匿名空间
|
||||
} // namespace
|
||||
|
||||
namespace ltlib
|
||||
{
|
||||
@@ -34,7 +34,7 @@ ServiceApp::~ServiceApp()
|
||||
|
||||
void ServiceApp::run()
|
||||
{
|
||||
SERVICE_TABLE_ENTRY dispatch_table[] = {
|
||||
SERVICE_TABLE_ENTRYW dispatch_table[] = {
|
||||
{ NULL, (LPSERVICE_MAIN_FUNCTIONW)service_main },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -60,7 +60,6 @@ bool ServiceApp::report_status(uint32_t current_state, uint32_t win32_exit_code,
|
||||
return SetServiceStatus(g_status_handle, &g_status) == TRUE;
|
||||
}
|
||||
|
||||
|
||||
void __stdcall ServiceApp::service_main()
|
||||
{
|
||||
std::wstring service_name;
|
||||
@@ -74,7 +73,6 @@ void __stdcall ServiceApp::service_main()
|
||||
report_status(SERVICE_STOPPED, NO_ERROR, 0);
|
||||
}
|
||||
|
||||
|
||||
void __stdcall ServiceApp::service_control_handler(unsigned long ctrl_code)
|
||||
{
|
||||
switch (ctrl_code) {
|
||||
|
||||
2
third_party/ffmpeg
vendored
2
third_party/ffmpeg
vendored
Submodule third_party/ffmpeg updated: 13c3454d4f...b06db7b514
Reference in New Issue
Block a user