Basic FreeType renderer implementation.
[gdipp.git] / gdipp_server / gdipp_server.cpp
blob815ac56a4074e780382366dc68ca3e596d227b8d
1 #include "stdafx.h"
2 #include "gdipp_config/constant_hook.h"
3 #include "gdipp_lib/helper.h"
4 #include "gdipp_server/global.h"
5 #include "gdipp_server/rpc_server.h"
7 namespace gdipp
10 #define SVC_NAME L"gdipp_svc"
12 SERVICE_STATUS svc_status = {};
13 SERVICE_STATUS_HANDLE h_svc_status = NULL;
15 HANDLE h_svc_events, h_wait_cleanup, h_rpc_thread;
17 std::map<ULONG, HANDLE> h_user_tokens;
18 std::map<ULONG, PROCESS_INFORMATION> pi_hooks_32, pi_hooks_64;
20 BOOL hook_proc(HANDLE h_user_token, char *hook_env_str, const wchar_t *gdipp_hook_name, PROCESS_INFORMATION &pi)
22 wchar_t gdipp_hook_path[MAX_PATH];
24 if (!get_dir_file_path(NULL, gdipp_hook_name, gdipp_hook_path))
25 return FALSE;
27 STARTUPINFOW si = {sizeof(STARTUPINFO)};
29 return CreateProcessAsUserW(h_user_token, gdipp_hook_path, NULL, NULL, NULL, TRUE, NULL, hook_env_str, NULL, &si, &pi);
32 BOOL start_hook(ULONG session_id)
34 // make the event handle inheritable
35 SECURITY_ATTRIBUTES inheritable_sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
36 BOOL b_ret, hook_success = TRUE;
37 HANDLE h_user_token;
39 b_ret = WTSQueryUserToken(session_id, &h_user_token);
40 if (!b_ret)
42 hook_success = FALSE;
43 goto post_hook;
46 // use the linked token if exists
47 // needed in UAC-enabled scenarios and Run As Administrator
48 TOKEN_LINKED_TOKEN linked_token;
49 DWORD token_info_len;
50 b_ret = GetTokenInformation(h_user_token, TokenLinkedToken, &linked_token, sizeof(TOKEN_LINKED_TOKEN), &token_info_len);
51 if (b_ret)
53 CloseHandle(h_user_token);
54 h_user_token = linked_token.LinkedToken;
57 char hook_env_str[64];
58 sprintf_s(hook_env_str, "gdipp_svc_proc_id=%d%c", GetCurrentProcessId(), 0);
60 if (!!config_instance.get_number(L"/gdipp/hook/include/proc_32_bit/text()", static_cast<int>(gdipp::hook_config::PROC_32_BIT)))
62 const wchar_t *gdipp_hook_name_32 = L"gdipp_hook_32.exe";
63 PROCESS_INFORMATION pi;
65 if (hook_proc(h_user_token, hook_env_str, gdipp_hook_name_32, pi))
66 pi_hooks_32[session_id] = pi;
67 else
68 hook_success = FALSE;
71 if (!!config_instance.get_number(L"/gdipp/hook/include/proc_64_bit/text()", static_cast<int>(gdipp::hook_config::PROC_64_BIT)))
73 const wchar_t *gdipp_hook_name_64 = L"gdipp_hook_64.exe";
74 PROCESS_INFORMATION pi;
76 if (hook_proc(h_user_token, hook_env_str, gdipp_hook_name_64, pi))
77 pi_hooks_64[session_id] = pi;
78 else
79 hook_success = FALSE;
82 post_hook:
83 if (hook_success)
85 h_user_tokens[session_id] = h_user_token;
87 else
89 if (h_user_token)
90 CloseHandle(h_user_token);
93 return b_ret;
96 void stop_hook(ULONG session_id)
98 std::map<ULONG, PROCESS_INFORMATION>::const_iterator pi_iter_32, pi_iter_64;
99 HANDLE h_hook_processes[2] = {};
101 pi_iter_32 = pi_hooks_32.find(session_id);
102 if (pi_iter_32 != pi_hooks_32.end())
103 h_hook_processes[0] = pi_iter_32->second.hProcess;
105 pi_iter_64 = pi_hooks_64.find(session_id);
106 if (pi_iter_64 != pi_hooks_64.end())
107 h_hook_processes[1] = pi_iter_64->second.hProcess;
109 // notify and wait hook subprocesses to exit
110 WaitForMultipleObjects(2, h_hook_processes, TRUE, INFINITE);
112 // clean up
114 if (pi_iter_32 != pi_hooks_32.end())
116 CloseHandle(pi_iter_32->second.hThread);
117 CloseHandle(pi_iter_32->second.hProcess);
118 pi_hooks_32.erase(pi_iter_32);
121 if (pi_iter_64 != pi_hooks_64.end())
123 CloseHandle(pi_iter_64->second.hThread);
124 CloseHandle(pi_iter_64->second.hProcess);
125 pi_hooks_64.erase(pi_iter_64);
128 CloseHandle(h_user_tokens[session_id]);
129 h_user_tokens.erase(session_id);
132 void set_svc_status(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
134 static DWORD dwCheckPoint = 1;
136 // fill in the SERVICE_STATUS structure
137 svc_status.dwCurrentState = dwCurrentState;
138 svc_status.dwWin32ExitCode = dwWin32ExitCode;
139 svc_status.dwWaitHint = dwWaitHint;
141 if (dwCurrentState == SERVICE_RUNNING)
142 svc_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SESSIONCHANGE;
143 else
144 svc_status.dwControlsAccepted = 0;
146 if (dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED)
147 svc_status.dwCheckPoint = 0;
148 else
149 svc_status.dwCheckPoint = ++dwCheckPoint;
151 // report the status of the service to the SCM
152 SetServiceStatus(h_svc_status, &svc_status);
155 DWORD WINAPI svc_ctrl_handler(DWORD dwCtrl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
157 BOOL b_ret;
159 // handle the requested control code
160 switch (dwCtrl)
162 case SERVICE_CONTROL_STOP:
163 set_svc_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
165 b_ret = SetEvent(h_svc_events);
166 assert(b_ret);
168 return NO_ERROR;
169 case SERVICE_CONTROL_INTERROGATE:
170 return NO_ERROR;
171 case SERVICE_CONTROL_SESSIONCHANGE:
172 if (dwEventType == WTS_SESSION_LOGON)
174 b_ret = start_hook(reinterpret_cast<WTSSESSION_NOTIFICATION *>(lpEventData)->dwSessionId);
176 if (b_ret)
177 return NO_ERROR;
178 else
179 return GetLastError();
181 else if (dwEventType == WTS_SESSION_LOGOFF)
183 stop_hook(reinterpret_cast<WTSSESSION_NOTIFICATION *>(lpEventData)->dwSessionId);
185 return NO_ERROR;
187 else
188 return ERROR_CALL_NOT_IMPLEMENTED;
189 default:
190 return ERROR_CALL_NOT_IMPLEMENTED;
194 VOID CALLBACK exit_cleanup(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
196 BOOL b_ret;
198 b_ret = UnregisterWait(h_wait_cleanup);
199 assert(b_ret || GetLastError() == ERROR_IO_PENDING);
201 b_ret = stop_gdipp_rpc_server();
202 if (b_ret)
204 const DWORD wait_ret = WaitForSingleObject(h_rpc_thread, INFINITE);
205 assert(wait_ret == WAIT_OBJECT_0);
208 set_svc_status(SERVICE_STOPPED, NO_ERROR, 0);
211 void svc_init()
213 h_svc_events = CreateEvent(NULL, TRUE, FALSE, NULL);
214 if (h_svc_events == NULL)
216 set_svc_status(SERVICE_STOPPED, NO_ERROR, 0);
217 return;
220 // clean up when event is set
221 if (!RegisterWaitForSingleObject(&h_wait_cleanup, h_svc_events, exit_cleanup, NULL, INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE))
223 set_svc_status(SERVICE_STOPPED, NO_ERROR, 0);
224 return;
227 // report running status when initialization is complete
228 set_svc_status(SERVICE_RUNNING, NO_ERROR, 0);
230 // initialize RPC for font service
231 h_rpc_thread = CreateThread(NULL, 0, start_gdipp_rpc_server, NULL, 0, NULL);
232 if (h_rpc_thread == NULL)
234 set_svc_status(SERVICE_STOPPED, NO_ERROR, 0);
235 return;
239 service process and its child processes run in session 0
240 some functions in gdipp may require interactive session (session ID > 0)
241 use CreateProcessAsUser to create process in the active user's session
243 const DWORD active_session_id = WTSGetActiveConsoleSessionId();
244 if (active_session_id != 0xFFFFFFFF)
245 start_hook(active_session_id);
248 VOID WINAPI svc_main(DWORD dwArgc, LPTSTR *lpszArgv)
250 // register the handler function for the service
251 h_svc_status = RegisterServiceCtrlHandlerExW(SVC_NAME, svc_ctrl_handler, NULL);
252 if (h_svc_status == NULL)
253 return;
255 // these SERVICE_STATUS members remain as set here
256 svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
257 svc_status.dwWin32ExitCode = NO_ERROR;
259 // report initial status to the SCM
260 set_svc_status(SERVICE_START_PENDING, NO_ERROR, 3000);
262 svc_init();
267 // #define svc_debug
269 int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
271 #ifdef svc_debug
272 Sleep(5000);
273 #endif // svc_debug
275 SERVICE_TABLE_ENTRY dispatch_table[] =
277 { SVC_NAME, gdipp::svc_main },
278 { NULL, NULL },
281 if (!StartServiceCtrlDispatcherW(dispatch_table))
282 return EXIT_FAILURE;
284 return EXIT_SUCCESS;