alroyso 3 年 前
コミット
c1f9bbdfcf

+ 72 - 1
SProxy/CApp.cpp

@@ -6,7 +6,7 @@ CApp* SSingleton<CApp>::ms_Singleton = NULL;
 
 CApp::CApp() : m_is_out(0), m_userinfo(nullptr), m_proxy_mode(PROXY_MODE::sys_mode), m_route_mode(ROUT_MODE::cn_mode)
 {
-
+	m_hInst = nullptr;
 }
 
 CApp::~CApp()
@@ -74,3 +74,74 @@ ROUT_MODE CApp::GetRouteMode()
 {
 	return m_route_mode;
 }
+
+void CApp::SetMethod(HINSTANCE hinst)
+{
+	m_hInst = hinst;
+}
+
+HINSTANCE CApp::GetMethod()
+{
+	return m_hInst;
+}
+
+void CApp::ChkeAndSetDtaPath()
+{
+	std::wstring  portableDataPath = std::filesystem::current_path().wstring() + L"\\" + DSPROXY_DATA_DIR_PORTABLE;
+
+	if (std::filesystem::is_directory(portableDataPath)) {
+		m_dataPath = std::move(portableDataPath);
+	}
+	else {
+		m_dataPath = GetKnownFolderFsPath(FOLDERID_RoamingAppData) / DSPROXY_DATA_DIR;
+	}
+	
+	m_configPath = m_dataPath / DSPROXY_CONFIG_DIR_NAME;
+	m_config_Gui_Path = m_dataPath;
+}
+
+bool CApp::CheckOnlyOneInstance() noexcept
+{
+	auto hFile = CreateFileW(m_dataPath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+	if (hFile == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_NOT_FOUND)
+		return false;
+	// Hold this handle
+	return true;
+}
+
+std::filesystem::path CApp::GetDataPath()
+{
+	return m_dataPath;
+}
+
+std::filesystem::path CApp::GetConfigPath()
+{
+	return m_configPath;
+}
+
+std::filesystem::path CApp::GetConfigGuiPath()
+{
+	return m_config_Gui_Path;
+}
+
+void CApp::SetupDataDirectory()
+{
+	try
+	{
+		CreateDirectoryIgnoreExist(m_configPath.c_str());
+		
+	}
+	CATCH_LOG();
+}
+
+void CApp::SetCLashRuning(bool m)
+{
+	m_clashRuning = m;
+}
+
+bool CApp::GetClashRuning()
+{
+	return m_clashRuning;
+}
+
+ 

+ 28 - 0
SProxy/CApp.h

@@ -28,6 +28,7 @@ struct RouteMode {
 	ROUT_MODE route_mode;
 	SStringW  name;
 };
+
 class CApp : public SSingleton<CApp>
 {
 public:
@@ -56,11 +57,38 @@ public:
 
 	ROUT_MODE GetRouteMode();
 
+	void SetMethod(HINSTANCE hinst);
+
+	HINSTANCE GetMethod();
+	
+	//¼ì²â»òÕßÉèÖÃDataĿ¼
+	void ChkeAndSetDtaPath();
+
+	bool CheckOnlyOneInstance() noexcept;
+
+	std::filesystem::path GetDataPath();
+	std::filesystem::path GetConfigPath();
+	std::filesystem::path GetConfigGuiPath();
+
+	void SetupDataDirectory();
+
+	void SetCLashRuning(bool m);
+
+	bool GetClashRuning();
+
+
 private:
 	int m_is_out;
 
 	CUserInfo*   m_userinfo;
 	PROXY_MODE m_proxy_mode;
 	ROUT_MODE  m_route_mode;
+	HINSTANCE  m_hInst;
+	std::filesystem::path m_dataPath;
+	std::filesystem::path m_configPath;
+	std::filesystem::path m_config_Gui_Path;
+
+	bool m_clashRuning = true;
+
 };
 

+ 81 - 52
SProxy/CLashConfig.cpp

@@ -39,9 +39,9 @@ BOOL CLashConfig::InitClash()
 	m_socks_port = CTool::getSingletonPtr()->FindAvailableTcpPort(9600,9800);
 	m_c_port = CTool::getSingletonPtr()->FindAvailableTcpPort(9900);
 
-    std::string path = std::filesystem::current_path().string();
+     
 
-	std::string dir = CLASHCONFIGDIR;
+	/*std::string dir = CLASHCONFIGDIR;
 
 	auto name_file = dir + "\\" + CLASHCONFIINIT;
 
@@ -49,11 +49,13 @@ BOOL CLashConfig::InitClash()
 	if (m_log == nullptr)
 	{
 		m_log = new Logger(Logger::file_and_terminal, Logger::info, logfile);
-	}
+	}*/
 	
+	auto name_file = DSPROXY_CONFIG_INIT_ClASH_NAME;
 
 	if (CApp::getSingletonPtr()->GetSysMode() == PROXY_MODE::tun_mode) {
-		name_file = dir + "\\" + CLASHCONFIINIT;
+		/*name_file = dir + "\\" + CLASHCONFIINIT;*/
+		name_file = DSPROXY_CONFIG_TUN_ClASH_NAME;
 	}
 	YAML::Node root;
 	root["port"] = m_http_port;
@@ -64,7 +66,8 @@ BOOL CLashConfig::InitClash()
 
 	try
 	{
-		std::ofstream ofstream(name_file);
+		auto config_path = CApp::getSingletonPtr()->GetConfigPath().wstring() + L"\\" + name_file;
+		std::ofstream ofstream(config_path);
 		ofstream << root << std::endl;
 		ofstream.close();
 		return TRUE;
@@ -80,37 +83,50 @@ BOOL CLashConfig::InitClash()
 
 BOOL CLashConfig::StartClash()
 {
-	std::wstring path = std::filesystem::current_path().wstring();
-	std::wstring config = path + L"\\" + WCLASHCONFIGDIR;
-	//args.push_back("-d");
-	////args.push_back(std::filesystem::current_path().string() + "\\route");
-	//args.push_back(path + "\\config");
-	//args.push_back("-f");
-	//args.push_back(path + "\\config\\configinit.yaml");
-	std::wstring run_config = config + L"\\" + S_CA2W(CLASHCONFIINIT).GetBuffer(0);
-	SStringW path_config;
-	path_config.Format(L"%s\\%s -d %s -f %s", config.c_str(),CLASHEXE,config.c_str(), run_config.c_str());
-	if (m_process)
+ 
+	//std::wstring config = path + L"\\" + WCLASHCONFIGDIR;
+	////args.push_back("-d");
+	//////args.push_back(std::filesystem::current_path().string() + "\\route");
+	////args.push_back(path + "\\config");
+	////args.push_back("-f");
+	////args.push_back(path + "\\config\\configinit.yaml");
+	//std::wstring run_config = config + L"\\" + S_CA2W(CLASHCONFIINIT).GetBuffer(0);
+	//SStringW path_config;
+	//path_config.Format(L"%s\\%s -d %s -f %s", config.c_str(),CLASHEXE,config.c_str(), run_config.c_str());
+	auto assetsDir = std::filesystem::current_path() / CLASH_ASSETS_DIR_NAME;
+	auto confg_path = CApp::getSingletonPtr()->GetConfigPath() / DSPROXY_CONFIG_INIT_ClASH_NAME;
+	ProcessManager::SetArgs(assetsDir / DSPROXY_EXE_NAME, assetsDir, std::move(confg_path));
+
+	/*ProcessManager::SetConfigFile(confg_path / DSPROXY_CONFIG_INIT_ClASH_NAME);*/
+	//m_Asyntask.AddTask(&CLashConfig::ThreadFun_process_Config, this, (LPARAM)NULL);
+
+
+	if (ProcessManager::Start())
+	{
+		CApp::getSingletonPtr()->SetCLashRuning(true);
+	}
+
+	/*if (m_process)
 	{
 		m_process->Create(CProcess::ASYNC);
 		if (m_process->Execute(path_config.GetBuffer(0)))
 		{
-			m_Asyntask.AddTask(&CLashConfig::ThreadFun_process_Config, this, (LPARAM)NULL);
+			
 		}
 
-		
-	}
+
+	}*/
     return 0;
 }
 
 BOOL CLashConfig::StopClash()
 {
 	m_is_qut = true;
-	m_Asyntask.Close();
-	char ch[MAX_PATH];
-	memset(ch, 0, MAX_PATH);
-	sprintf_s(ch, "ok\n");
-	m_process->WriteSome(ch, sizeof(ch));
+	ProcessManager::Stop();
+	//char ch[MAX_PATH];
+	//memset(ch, 0, MAX_PATH);
+	//sprintf_s(ch, "ok\n");
+	//m_process->WriteSome(ch, sizeof(ch));
     return 0;
 }
 
@@ -156,34 +172,47 @@ std::vector<YAML::Node> CLashConfig::buildRules()
 
 void CLashConfig::ThreadFun_process_Config(LPARAM lParam)
 {
-	if (m_process)
+
+	if (ProcessManager::Start())
 	{
-		char ch[MAX_PATH];
-		memset(ch, 0, MAX_PATH);
-		do
-		{
-			size_t bytesRead = m_process->ReadLine(ch, sizeof(ch));
-			
-			if (bytesRead == 0)
-			{
-				
-				break;
-			}
-
-			if (m_is_qut)
-			{
-				
-				break;
-			}
-
-			if (m_log)
-			{
-				std::string log(ch);
-				m_log->INFO(log);
-			}
-
-			/*if (ch[bytesRead - 1] == '\n') break;*/
-		} while (true);
-		
+		CApp::getSingletonPtr()->SetCLashRuning(true);
+
+		HANDLE hClashProcess = ProcessManager::GetClashProcessInfo().hProcess;
+
+		for (size_t i = 0; i < 5; ++i) {
+
+		}
 	}
+
+
+	//if (m_process)
+	//{
+	//	//char ch[MAX_PATH];
+	//	//memset(ch, 0, MAX_PATH);
+	//	//do
+	//	//{
+	//	//	size_t bytesRead = m_process->ReadLine(ch, sizeof(ch));
+	//	//	
+	//	//	if (bytesRead == 0)
+	//	//	{
+	//	//		
+	//	//		break;
+	//	//	}
+
+	//	//	if (m_is_qut)
+	//	//	{
+	//	//		
+	//	//		break;
+	//	//	}
+
+	//	//	if (m_log)
+	//	//	{
+	//	//		std::string log(ch);
+	//	//		m_log->INFO(log);
+	//	//	}
+
+	//	//	/*if (ch[bytesRead - 1] == '\n') break;*/
+	//	//} while (true);
+	//	
+	//}
 }

+ 2 - 0
SProxy/CLashConfig.h

@@ -5,6 +5,8 @@
 #include "CTool.h"
 #include "CProcess.h"
 #include "Logger.h"
+ 
+
 class CLashConfig : public IBaseClash
 {
 public:

+ 37 - 22
SProxy/CManageNetWork.cpp

@@ -130,40 +130,55 @@ void CManageNetWork::ThreadFun_Dowlon_Config(LPARAM lParam)
 }
 
 void CManageNetWork::LoadFileToData() {
-	FileOperate f;
-	std::string path = std::filesystem::current_path().string();
-	if (f.open(path + "\\user.json", std::fstream::in | std::fstream::out | std::fstream::app)) {
-		std::string data = f.readAll();
-		if (data.empty())
-		{
-			return;
-		}
-		nlohmann::json j3 = nlohmann::json::parse(data.begin(), data.end());
-		this->m_username = j3["username"].get<std::string>().c_str();
-		this->m_password = j3["password"].get<std::string>().c_str();
 
+	try
+	{
+		FileOperate f;
+		SStringA   paht;
+
+		paht.Format("%s\\%s", CApp::getSingletonPtr()->GetDataPath().string().c_str(), S_CW2A(DSPROXY_CONFIG_NAME, CP_UTF8));
+
+		if (f.open(paht.GetBuffer(0), std::fstream::in | std::fstream::out | std::fstream::app)) {
+			std::string data = f.readAll();
+			if (data.empty())
+			{
+				return;
+			}
+			nlohmann::json j3 = nlohmann::json::parse(data.begin(), data.end());
+			this->m_username = j3["username"].get<std::string>().c_str();
+			this->m_password = j3["password"].get<std::string>().c_str();
+
+		}
+		f.close();
 	}
-	f.close();
+	CATCH_LOG();
+	
 }
 
 void CManageNetWork::DataToFile()
 {
+	try
+	{
+		nlohmann::json j3;
+		j3["username"] = this->m_username;
+		j3["password"] = this->m_password;
 
-	nlohmann::json j3;
-	j3["username"] = this->m_username;
-	j3["password"] = this->m_password;
+		auto str = j3.dump(4);
 
-	auto str = j3.dump(4);
+		FileOperate f;
+		SStringA   paht;
 
-	FileOperate f;
+		paht.Format("%s\\%s", CApp::getSingletonPtr()->GetDataPath().string().c_str(), S_CW2A(DSPROXY_CONFIG_NAME, CP_UTF8));
 
-	std::string path = std::filesystem::current_path().string();
 
-	f.create(path + "\\user.json");
+		f.create(paht.GetBuffer(0));
+
+		if (f.open(paht.GetBuffer(0), std::fstream::in | std::fstream::out | std::fstream::app)) {
+			f.write(str);
+		}
 
-	if (f.open(path + "\\user.json", std::fstream::in | std::fstream::out | std::fstream::app)) {
-		f.write(str);
+		f.close();
 	}
+	CATCH_LOG();
 
-	f.close();
 }

+ 3 - 3
SProxy/CNetWork.cpp

@@ -93,8 +93,8 @@ bool  write_data(std::string /*data*/, intptr_t /*userdata*/)
 
 bool CNetWork::Download(std::string path)
 {
-
-	std::string configdir = std::filesystem::current_path().string() + "\\" + CLASHCONFIGDIR;
+	return false;
+	/*std::string configdir = std::filesystem::current_path().string() + "\\" + CLASHCONFIGDIR;
 
 	std::filesystem::path p(configdir);
 
@@ -109,7 +109,7 @@ bool CNetWork::Download(std::string path)
 	cpr::Session session;
 	session.SetUrl(url);
 	cpr::Response response = session.Download(ofs);
-	return response.status_code == 200;
+	return response.status_code == 200;*/
 }
 
 

BIN
SProxy/MainDlg.cpp


+ 44 - 0
SProxy/PortableModeUtil.hpp

@@ -0,0 +1,44 @@
+#pragma once
+# ifndef PORTMODE_H
+# define PORTMODE_H
+
+
+namespace PortableModeUtil
+{
+	void CheckAndSetDataPath()
+	{
+		
+
+		/*auto portableDataPath = std::filesystem::current_path().wstring()  +  DSPROXY_DATA_DIR_PORTABLE;
+		if (fs::is_directory(portableDataPath))
+		{
+			g_portableMode = true;
+			g_dataPath = std::move(portableDataPath);
+		}
+		else
+			g_dataPath = GetKnownFolderFsPath(FOLDERID_RoamingAppData) / CLASHXW_DATA_DIR;
+		g_configPath = g_dataPath / CLASH_CONFIG_DIR_NAME;*/
+	}
+
+
+	inline void SetAppId()
+	{
+
+		THROW_IF_FAILED(SetCurrentProcessExplicitAppUserModelID(CLASHXW_APP_ID));
+
+
+		//if (g_portableMode)
+		//{
+		//	wchar_t appId[std::size(CLASHXW_APP_ID) + guidSize] = CLASHXW_APP_ID L"/"; // Both size includes the null terminator
+		//	GUID guid = {};
+		//	THROW_IF_FAILED(CoCreateGuid(&guid));
+		//	THROW_HR_IF(E_OUTOFMEMORY, StringFromGUID2(guid, appId + std::size(CLASHXW_APP_ID), guidSize) != guidSize);
+		//	THROW_IF_FAILED(SetCurrentProcessExplicitAppUserModelID(appId));
+		//}
+		//else
+		//	THROW_IF_FAILED(SetCurrentProcessExplicitAppUserModelID(CLASHXW_APP_ID));
+	}
+}
+
+
+#endif

+ 350 - 0
SProxy/ProcessManager.hpp

@@ -0,0 +1,350 @@
+#pragma once
+# ifndef PROCESS_H
+# define PROCESS_H
+
+
+#define OBJPREFIX LR"(Local\)"
+constexpr size_t guidSize = std::size(L"{00000000-0000-0000-0000-000000000000}"); // including the null terminator
+constexpr size_t prefixSize = std::size(OBJPREFIX) - 1;
+constexpr size_t objNameSize = prefixSize + guidSize;
+
+struct ProcessInfo
+{
+	DWORD processId;
+	DWORD threadId;
+	HWND hWndConsole;
+};
+
+namespace ProcessManager
+{
+	enum struct State
+	{
+		Stop,
+		Running,
+		WaitStop
+	};
+
+	namespace {
+		State _state = State::Stop;
+		std::filesystem::path _exePath;
+		std::filesystem::path _homeDir;
+		std::filesystem::path _configFile;
+		std::wstring _ctlAddr, _ctlSecret, _clashCmd;
+		wil::unique_handle _hJob;
+		wil::unique_process_information _subProcInfo, _clashProcInfo;
+		HWND _hWndConsole = nullptr;
+		wil::unique_handle _hEvent;
+		HINSTANCE mInstance;
+ 
+		void _UpdateClashCmd()
+		{
+			_clashCmd.assign(LR"(")");
+			_clashCmd.append(_exePath.filename());
+			_clashCmd.append(LR"(")");
+			if (!_homeDir.empty())
+			{
+				_clashCmd.append(LR"( -d ")");
+				_clashCmd.append(_homeDir);
+				_clashCmd.append(LR"(")");
+			}
+			if (!_configFile.empty())
+			{
+				_clashCmd.append(LR"( -f ")");
+				_clashCmd.append(_configFile);
+				_clashCmd.append(LR"(")");
+			}
+			//if (!_uiDir.empty())
+			//{
+			//	_clashCmd.append(LR"( -ext-ui ")");
+			//	_clashCmd.append(_uiDir);
+			//	_clashCmd.append(LR"(")");
+			//}
+			//if (!_ctlAddr.empty())
+			//{
+			//	_clashCmd.append(LR"( -ext-ctl ")");
+			//	_clashCmd.append(_ctlAddr);
+			//	_clashCmd.append(LR"(")");
+			//}
+
+			//// Override secret even if empty
+			//_clashCmd.append(LR"( -secret ")");
+			//_clashCmd.append(_ctlSecret);
+			//_clashCmd.append(LR"(")");
+		}
+
+	}
+
+	inline void SetInsTanCe(HINSTANCE m) {
+		mInstance = m;
+	}
+
+	 void SetArgs(std::filesystem::path exePath, std::filesystem::path homeDir, std::filesystem::path cconfigFile)
+	{
+
+	 
+
+		_exePath = exePath;
+		_homeDir = homeDir;
+		_configFile = cconfigFile;
+		//_uiDir = "";
+		//_ctlAddr = L"";
+		//_ctlSecret = L"";
+
+		_UpdateClashCmd();
+	}
+
+	 void SetConfigFile(std::filesystem::path configFile)
+	{
+		_configFile = configFile;
+
+		_UpdateClashCmd();
+	}
+
+	inline State IsRunning() { return _state; }
+	inline const PROCESS_INFORMATION& GetSubProcessInfo() { return _subProcInfo; }
+	inline const PROCESS_INFORMATION& GetClashProcessInfo() { return _clashProcInfo; }
+	inline HWND GetConsoleWindow() { return _hWndConsole; }
+
+	inline bool Start()
+	{
+		if (_state != State::Stop)
+			return false;
+
+		try
+		{
+			_hJob.reset(CreateJobObjectW(nullptr, nullptr));
+			THROW_LAST_ERROR_IF_NULL(_hJob);
+			JOBOBJECT_BASIC_LIMIT_INFORMATION bli;
+			bli.LimitFlags = JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+			JOBOBJECT_EXTENDED_LIMIT_INFORMATION eli;
+			eli.BasicLimitInformation = bli;
+			THROW_IF_WIN32_BOOL_FALSE(SetInformationJobObject(_hJob.get(), JobObjectExtendedLimitInformation, &eli, sizeof(eli)));
+			
+			wchar_t objName[objNameSize + 1] = OBJPREFIX;
+			GUID guid = {};
+			THROW_IF_FAILED(CoCreateGuid(&guid));
+			THROW_HR_IF(E_OUTOFMEMORY, StringFromGUID2(guid, objName + prefixSize, guidSize) != guidSize);
+
+			size_t s = (_exePath.native().size() + 1) + (_clashCmd.size() + 1) * sizeof(wchar_t);
+			size_t l = sizeof(ProcessInfo);
+			size_t size = max(s, l);
+
+			objName[objNameSize - 1] = L'F';
+			wil::unique_handle hFileMapping(CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, static_cast<DWORD>(size), objName));
+			THROW_LAST_ERROR_IF_NULL(hFileMapping);
+			auto error = GetLastError();
+			if (error == ERROR_ALREADY_EXISTS) THROW_WIN32(error);
+
+			wil::unique_mapview_ptr<wchar_t> buffer(reinterpret_cast<wchar_t*>(MapViewOfFile(hFileMapping.get(), FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0)));
+			THROW_LAST_ERROR_IF_NULL(buffer);
+
+
+			objName[objNameSize - 1] = L'E';
+			wil::unique_handle hEvent(CreateEventW(nullptr, FALSE, FALSE, objName));
+			THROW_LAST_ERROR_IF_NULL(hEvent);
+			error = GetLastError();
+			if (error == ERROR_ALREADY_EXISTS) THROW_WIN32(error);
+
+			auto exePathPtr = buffer.get();
+			size_t i = _exePath.native().copy(exePathPtr, std::filesystem::path::string_type::npos);
+			exePathPtr[i] = 0;
+
+			auto cmdPtr = buffer.get() + i + 1;
+			i = _clashCmd.copy(cmdPtr, std::wstring::npos);
+			cmdPtr[i] = 0;
+
+
+			auto selfPath = GetModuleFsPath(mInstance);
+			auto guidStr = objName + prefixSize;
+			std::wstring cmd(LR"(")");
+			cmd.append(selfPath);
+			cmd.append(LR"(" --pm=)");
+			cmd.append(guidStr, guidSize - 1);
+
+			{
+				// Disable Windows Error Reporting for subprocess
+				auto lastErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
+				auto restoreErrorMode = wil::scope_exit([=]() { // Restore error mode after CreateProcessW
+					SetErrorMode(lastErrorMode);
+					});
+				wil::unique_cotaskmem_string appId;
+				THROW_IF_FAILED(GetCurrentProcessExplicitAppUserModelID(&appId));
+
+				/*wil::unique_cotaskmem_string appId;
+				THROW_IF_FAILED(GetCurrentProcessExplicitAppUserModelID(&appId));*/
+				/*STARTUPINFOW si = {
+					.cb = sizeof(si),
+					.lpTitle = appId.get(),
+					.dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_PREVENTPINNING | STARTF_TITLEISAPPID | STARTF_USESHOWWINDOW,
+					.wShowWindow = SW_HIDE
+				};*/
+
+				STARTUPINFOW si;
+				si.cb = sizeof(si);
+				si.lpTitle = appId.get();
+				si.dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_PREVENTPINNING | STARTF_TITLEISAPPID | STARTF_USESHOWWINDOW;
+				si.wShowWindow = SW_HIDE;
+				THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(selfPath.c_str(), cmd.data(), nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &_subProcInfo));
+			}
+
+			// Ensure process in job before start
+			THROW_IF_WIN32_BOOL_FALSE(AssignProcessToJobObject(_hJob.get(), _subProcInfo.hProcess));
+			THROW_LAST_ERROR_IF(ResumeThread(_subProcInfo.hThread) == static_cast<DWORD>(-1));
+
+			HANDLE handles[] = { hEvent.get(), _subProcInfo.hProcess };
+			auto ret = WaitForMultipleObjects(static_cast<DWORD>(std::size(handles)), handles, FALSE, INFINITE);
+			error = ERROR_TIMEOUT;
+
+			switch (ret)
+			{
+			case WAIT_OBJECT_0: // Event signaled, clash process started suspended
+			{
+				auto info = reinterpret_cast<ProcessInfo*>(buffer.get());
+				_clashProcInfo.dwProcessId = info->processId;
+				_clashProcInfo.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, _clashProcInfo.dwProcessId);
+				THROW_LAST_ERROR_IF_NULL(_clashProcInfo.hProcess);
+
+				_clashProcInfo.dwThreadId = info->threadId;
+				_clashProcInfo.hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, _clashProcInfo.dwThreadId);
+				THROW_LAST_ERROR_IF_NULL(_clashProcInfo.hThread);
+
+				_hWndConsole = info->hWndConsole;
+			}
+			break;
+			case WAIT_OBJECT_0 + 1: // Sub process exited before event signaled
+			{
+				DWORD exitCode;
+				THROW_IF_WIN32_BOOL_FALSE(GetExitCodeProcess(_subProcInfo.hProcess, &exitCode));
+				HRESULT hr = static_cast<HRESULT>(exitCode); // Treat exit code as hresult
+				if (SUCCEEDED(hr)) hr = E_UNEXPECTED;
+				THROW_HR(hr);
+			}
+			return false;
+			case WAIT_FAILED:
+				error = GetLastError();
+				[[fallthrough]];
+			case WAIT_TIMEOUT:
+				THROW_WIN32(error);
+				return false;
+			}
+			_hEvent = std::move(hEvent);
+			ResumeThread(_clashProcInfo.hThread);
+		}
+		catch (...)
+		{
+			_hJob.reset();
+			LOG_CAUGHT_EXCEPTION();
+			return false;
+		}
+		_state = State::Running;
+		return true;
+	}
+
+
+	inline void Stop()
+	{
+		if (_state != State::Stop)
+		{
+			_state = State::Stop;
+			_hJob.reset();
+			_subProcInfo.reset();
+			_clashProcInfo.reset();
+			_hWndConsole = nullptr;
+			_hEvent.reset();
+		}
+	}
+
+	inline void SendStopSignal()
+	{
+		if (_state == State::Running)
+		{
+			_state = State::WaitStop;
+			SetEvent(_hEvent.get());
+		}
+	}
+
+
+	inline int SubProcess(std::wstring_view guid)
+	{
+		try
+		{
+			wchar_t objName[objNameSize + 1] = OBJPREFIX;
+			size_t i = guid.copy(objName + prefixSize, guidSize - 1);
+			objName[prefixSize + i] = L'F';
+
+			wil::unique_handle hFileMapping(OpenFileMappingW(FILE_MAP_WRITE | FILE_MAP_READ, FALSE, objName));
+			THROW_LAST_ERROR_IF_NULL(hFileMapping);
+
+			wil::unique_mapview_ptr<wchar_t> buffer(reinterpret_cast<wchar_t*>(MapViewOfFile(hFileMapping.get(), FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0)));
+			THROW_LAST_ERROR_IF_NULL(buffer);
+
+			objName[prefixSize + i] = L'E';
+			wil::unique_handle hEvent(OpenEventW(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, objName));
+			THROW_LAST_ERROR_IF_NULL(hEvent);
+
+			THROW_IF_WIN32_BOOL_FALSE(AllocConsole());
+			HWND hWndConsole = ::GetConsoleWindow();
+			THROW_LAST_ERROR_IF_NULL(hWndConsole);
+			ShowWindow(hWndConsole, SW_HIDE);
+
+			auto exePath = buffer.get();
+			auto clashCmd = buffer.get() + wcslen(exePath) + 1;
+		 
+			STARTUPINFOW si;
+			si.cb = sizeof(si);
+			wil::unique_process_information procInfo;
+			THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(exePath, clashCmd, nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &procInfo));
+
+			auto info = reinterpret_cast<ProcessInfo*>(buffer.get());
+			/**info = {
+				.processId = procInfo.dwProcessId,
+				.threadId = procInfo.dwThreadId,
+				.hWndConsole = hWndConsole
+			};*/
+
+			info->processId = procInfo.dwProcessId;
+			info->threadId = procInfo.dwThreadId;
+			info->hWndConsole = hWndConsole;
+
+
+			SetConsoleCtrlHandler(nullptr, TRUE); // Ignores Ctrl+C
+
+			THROW_IF_WIN32_BOOL_FALSE(SetEvent(hEvent.get()));
+
+			while (true)
+			{
+				HANDLE handles[] = { hEvent.get(), procInfo.hProcess };
+				auto ret = WaitForMultipleObjects(static_cast<DWORD>(std::size(handles)), handles, FALSE, INFINITE);
+				if (ret == WAIT_OBJECT_0) // Event signaled
+				{
+					GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
+				}
+				else if (ret == WAIT_OBJECT_0 + 1) // Clash process exited
+				{
+					break;
+				}
+				else if (ret == WAIT_FAILED)
+				{
+					THROW_LAST_ERROR();
+				}
+				else if (ret == WAIT_TIMEOUT)
+				{
+					THROW_WIN32(ERROR_TIMEOUT);
+				}
+			}
+
+			const std::wstring_view msg = (
+				L"\n"
+				L"[Process completed]\n");
+			THROW_IF_WIN32_BOOL_FALSE(WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), msg.data(), static_cast<DWORD>(msg.size()), nullptr, nullptr));
+			static_cast<void>(_getch());
+		}
+		CATCH_RETURN();
+		return S_OK;
+	}
+}
+
+
+#undef OBJPREFIX
+
+# endif

BIN
SProxy/SProxy.cpp


+ 4 - 1
SProxy/SProxy.vcxproj

@@ -144,7 +144,7 @@
       <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
       <WarningLevel>Level3</WarningLevel>
       <AdditionalIncludeDirectories>$(SOUIPATH)\config;$(SOUIPATH)\components;$(SOUIPATH)\SOUI\include;$(SOUIPATH)\utilities\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>WIN64;_WINDOWS;STRICT;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WIN64;_WINDOWS;STRICT;_DEBUG;WINVER=0x0600;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
       <BrowseInformation>true</BrowseInformation>
       <Optimization>Disabled</Optimization>
@@ -236,6 +236,8 @@
     <ClInclude Include="Logger.h" />
     <ClInclude Include="LoginDlg.h" />
     <ClInclude Include="MainDlg.h" />
+    <ClInclude Include="PortableModeUtil.hpp" />
+    <ClInclude Include="ProcessManager.hpp" />
     <ClInclude Include="resource.h" />
     <ClInclude Include="SouiInit.h" />
     <ClInclude Include="SRadioBox2.h" />
@@ -243,6 +245,7 @@
     <ClInclude Include="SShellNotifyIcon.h" />
     <ClInclude Include="StabtypeControl.h" />
     <ClInclude Include="stdafx.h" />
+    <ClInclude Include="Util.hpp" />
   </ItemGroup>
   <ItemGroup>
     <None Include="res\soui_res.rc2" />

+ 9 - 0
SProxy/SProxy.vcxproj.filters

@@ -171,6 +171,15 @@
     <ClInclude Include="Logger.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="ProcessManager.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="PortableModeUtil.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Util.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\soui_res.rc2">

+ 8 - 1
SProxy/SouiInit.h

@@ -1,4 +1,11 @@
-class SUserObjectDefaultRegister : public TObjRefImpl<ISystemObjectRegister>
+
+ 
+
+
+
+
+
+class SUserObjectDefaultRegister : public TObjRefImpl<ISystemObjectRegister>
 {
 public:
 	void RegisterWindows(SObjectFactoryMgr *objFactory) const;

+ 198 - 0
SProxy/Util.hpp

@@ -0,0 +1,198 @@
+#pragma once
+// https://msdn.microsoft.com/en-us/magazine/mt763237
+inline std::wstring Utf8ToUtf16(std::string_view utf8)
+{
+	if (utf8.empty())
+	{
+		return {};
+	}
+
+	constexpr DWORD kFlags = MB_ERR_INVALID_CHARS;
+	const int utf8Length = static_cast<int>(utf8.length());
+	const int utf16Length = MultiByteToWideChar(
+		CP_UTF8,
+		kFlags,
+		utf8.data(),
+		utf8Length,
+		nullptr,
+		0
+	);
+	THROW_LAST_ERROR_IF(utf16Length == 0);
+
+	std::wstring utf16(utf16Length, L'\0');
+	const int result = MultiByteToWideChar(
+		CP_UTF8,
+		kFlags,
+		utf8.data(),
+		utf8Length,
+		utf16.data(),
+		utf16Length
+	);
+	THROW_LAST_ERROR_IF(result == 0);
+
+	return utf16;
+}
+
+inline std::string Utf16ToUtf8(std::wstring_view utf16)
+{
+	if (utf16.empty())
+	{
+		return {};
+	}
+
+	constexpr DWORD kFlags = WC_ERR_INVALID_CHARS;
+	const int utf16Length = static_cast<int>(utf16.length());
+	const int utf8Length = WideCharToMultiByte(
+		CP_UTF8,
+		kFlags,
+		utf16.data(),
+		utf16Length,
+		nullptr,
+		0,
+		nullptr, nullptr
+	);
+	THROW_LAST_ERROR_IF(utf8Length == 0);
+
+	std::string utf8(utf8Length, '\0');
+	const int result = WideCharToMultiByte(
+		CP_UTF8,
+		kFlags,
+		utf16.data(),
+		utf16Length,
+		utf8.data(),
+		utf8Length,
+		nullptr, nullptr
+	);
+	THROW_LAST_ERROR_IF(result == 0);
+
+	return utf8;
+}
+
+// https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/author-coclasses#add-helper-types-and-functions
+// License: see the https://github.com/MicrosoftDocs/windows-uwp/blob/docs/LICENSE-CODE file
+inline auto GetModuleFsPath(HMODULE hModule)
+{
+	std::wstring path(MAX_PATH, L'\0');
+	DWORD actualSize;
+
+	while (1)
+	{
+		actualSize = GetModuleFileNameW(hModule, path.data(), static_cast<DWORD>(path.size()));
+
+		if (static_cast<size_t>(actualSize) + 1 > path.size())
+			path.resize(path.size() * 2);
+		else
+			break;
+	}
+
+	path.resize(actualSize);
+	return std::filesystem::path(path);
+}
+
+inline auto GetKnownFolderFsPath(REFKNOWNFOLDERID rfid)
+{
+	wil::unique_cotaskmem_string path;
+	SHGetKnownFolderPath(rfid, 0, nullptr, &path);
+	return std::filesystem::path(path.get()).concat(L"\\");
+}
+
+inline void CreateDirectoryIgnoreExist(const wchar_t* path)
+{
+	if (!CreateDirectoryW(path, nullptr))
+	{
+		auto lastErr = GetLastError();
+		if (lastErr != ERROR_ALREADY_EXISTS)
+			THROW_WIN32(lastErr);
+	}
+}
+
+inline void CreateShellLink(const wchar_t* linkPath, const wchar_t* target)
+{
+	/*auto shellLink = wil::CoCreateInstance<IShellLinkW>(CLSID_ShellLink);
+	THROW_IF_FAILED(shellLink->SetPath(target));
+
+	auto persistFile = shellLink.query<IPersistFile>();
+	THROW_IF_FAILED(persistFile->Save(linkPath, TRUE));*/
+}
+
+inline void SetClipboardText(std::wstring_view text,HWND m)
+{
+	try
+	{
+		THROW_IF_WIN32_BOOL_FALSE(OpenClipboard(m));
+		THROW_IF_WIN32_BOOL_FALSE(EmptyClipboard());
+
+		auto hGlobal = GlobalAlloc(GMEM_MOVEABLE, (text.size() + 1) * sizeof(wchar_t));
+		THROW_LAST_ERROR_IF_NULL(hGlobal);
+
+		{
+			wil::unique_hglobal_locked ptr(hGlobal);
+			THROW_LAST_ERROR_IF_NULL(ptr.get());
+
+			auto str = static_cast<wchar_t*>(ptr.get());
+			auto len = text.copy(str, text.size());
+			str[len] = 0;
+		}
+
+		SetClipboardData(CF_UNICODETEXT, hGlobal);
+	}
+	CATCH_LOG();
+	CloseClipboard();
+}
+
+inline std::wstring GetClipboardText(HWND m)
+{
+	std::wstring result;
+	try
+	{
+		if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
+			return result;
+
+		THROW_IF_WIN32_BOOL_FALSE(OpenClipboard(m));
+
+		auto hGlobal = GetClipboardData(CF_UNICODETEXT);
+		THROW_LAST_ERROR_IF_NULL(hGlobal);
+
+		wil::unique_hglobal_locked ptr(hGlobal);
+		THROW_LAST_ERROR_IF_NULL(ptr.get());
+
+		result = static_cast<wchar_t*>(ptr.get());
+	}
+	CATCH_LOG();
+	CloseClipboard();
+	return result;
+}
+
+inline bool IsUrlVaild(const wchar_t* urlStr)
+{
+	try
+	{
+		/*if (*urlStr == 0)
+			return false;
+
+		skyr::url url{ std::wstring_view(urlStr) };
+
+		if (url.empty())
+			return false;
+
+		if (url.hostname().empty())
+			return false;
+
+		return url.scheme() == "http" || url.scheme() == "https";*/
+	}
+	CATCH_LOG();
+	return false;
+}
+
+inline std::wstring GetWindowString(HWND hWnd)
+{
+	std::wstring str;
+	int len = GetWindowTextLengthW(hWnd);
+	if (len != 0)
+	{
+		str.resize(static_cast<size_t>(len) + 1);
+		len = GetWindowTextW(hWnd, str.data(), len + 1);
+		str.resize(static_cast<size_t>(len));
+	}
+	return str;
+}

+ 10 - 8
SProxy/comm.h

@@ -1,15 +1,17 @@
 #pragma once
 
-#define CLASHCONFIGDIR "config"
-#define WCLASHCONFIGDIR L"config"
-#define CLASHCONFIGNAME "sysconfig.yml"
-#define WCLASHCONFIGNAME L"sysconfig.yml"
+#define CLASHXW_APP_ID L"com.dsproxy.sproxy"
 
+constexpr auto DSPROXY_DATA_DIR = L"DSProxy";
+constexpr auto DSPROXY_DATA_DIR_PORTABLE = L"Data";
+constexpr auto DSPROXY_CONFIG_NAME = L"DSProxy.json";
 
-#define CLASHCONFIINIT "config.yml"
+constexpr auto CLASH_ASSETS_DIR_NAME = L"Assets";
 
+constexpr auto DSPROXY_CONFIG_INIT_ClASH_NAME = L"config_info.yaml";
+constexpr auto DSPROXY_CONFIG_ClASH_NAME = L"config.yaml";
+constexpr auto DSPROXY_CONFIG_TUN_ClASH_NAME = L"config.yaml";
 
-#define CLASHCONFIGTUNNAME "tunconfig.yml"
 
-
-#define CLASHEXE L"syscode.exe"
+constexpr auto DSPROXY_CONFIG_DIR_NAME = L"Config";
+constexpr auto DSPROXY_EXE_NAME = L"syscode.exe";

BIN
SProxy/res/resource.h


BIN
SProxy/res/soui_res.rc2


BIN
SProxy/stdafx.h