svvitch
digital signage player
|
00001 // 00002 // switch.cpp 00003 // アプリケーションの実装 00004 // 00005 00006 #include <winsock2.h> 00007 #include <windows.h> 00008 #include <psapi.h> 00009 #include <Dbt.h> 00010 00011 #include <Poco/format.h> 00012 #include <Poco/Logger.h> 00013 #include <Poco/UnicodeConverter.h> 00014 #include <Poco/Net/HTTPServer.h> 00015 #include <Poco/Net/HTTPServerParams.h> 00016 #include <Poco/Net/ServerSocket.h> 00017 #include <Poco/Net/HTTPStreamFactory.h> 00018 #include <Poco/Net/HTTPSStreamFactory.h> 00019 #include <Poco/Net/KeyConsoleHandler.h> 00020 #include <Poco/Net/ConsoleCertificateHandler.h> 00021 #include <Poco/Net/SSLManager.h> 00022 #include <Poco/SharedPtr.h> 00023 #include <Poco/Net/SSLManager.h> 00024 00025 #include "switch.h" 00026 #include "Renderer.h" 00027 #include "CaptureScene.h" 00028 #include "MainScene.h" 00029 #include "DiffDetectScene.h" 00030 //#include "UserInterfaceScene.h" 00031 #include "Utils.h" 00032 #include "WebAPI.h" 00033 //#include "ui/UserInterfaceManager.h" 00034 00035 //#ifndef _DEBUG 00036 //#include <omp.h> 00037 //#endif 00038 00039 #define _CRTDBG_MAP_ALLOC 00040 #include <crtdbg.h> // 最後にインクルードしたほうがいい気がする。C++の場合。標準ヘッダの中にnewとかあると変になる?? 00041 #ifdef _DEBUG 00042 #define new ::new(_NORMAL_BLOCK,__FILE__,__LINE__) // これが重要 00043 #endif 00044 00045 00046 static TCHAR clsName[] = TEXT("switchClass"); // クラス名 00047 00048 static Configuration _conf; 00049 00050 static RendererPtr _renderer; 00051 //static ui::UserInterfaceManagerPtr _uim; 00052 00053 //static string _interruptFile; 00054 00055 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { 00056 #if defined(DEBUG) | defined(_DEBUG) 00057 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 00058 // _CrtSetBreakAlloc(4141); 00059 // _CrtSetBreakAlloc(3627); 00060 // _CrtSetBreakAlloc(7048); 00061 #endif 00062 #if defined(DEBUG) | defined(_DEBUG) 00063 #endif 00064 #ifdef _DEBUG 00065 //_CrtDumpMemoryLeaks(); 00066 #endif 00067 00068 _conf.initialize(); 00069 Poco::Logger& log = Poco::Logger::get(""); 00070 #ifdef _DEBUG 00071 log.information("*** system start (debug)"); 00072 #else 00073 //#pragma omp parallel 00074 //{ 00075 // log.information(Poco::format("*** system start (omp threads x%d)", omp_get_num_threads())); 00076 //} 00077 #endif 00078 vector<string> args; 00079 svvitch::split(lpCmdLine, ' ', args); 00080 for (vector<string>::iterator it = args.begin(); it != args.end(); it++) { 00081 string opt = Poco::toLower(*it); 00082 if (opt == "-w") { 00083 it++; 00084 if (it != args.end()) { 00085 long t = Poco::NumberParser::parse(*it); 00086 log.information(Poco::format("startup waiting: %lds", t)); 00087 Poco::Thread::sleep(t * 1000); 00088 } 00089 } 00090 } 00091 00092 HWND hWnd; 00093 MSG msg; 00094 // ウィンドウクラスの初期化 00095 WNDCLASSEX wcex = { 00096 sizeof(WNDCLASSEX), // この構造体のサイズ 00097 CS_DBLCLKS, // ウインドウのスタイル(default) 00098 WindowProc, // メッセージ処理関数の登録 00099 0, // 通常は使わないので常に0 00100 0, // 通常は使わないので常に0 00101 hInstance, // インスタンスへのハンドル 00102 NULL, // アイコン(なし) 00103 LoadCursor(NULL, IDC_ARROW), // カーソルの形 00104 NULL, NULL, // 背景なし、メニューなし 00105 clsName, // クラス名の指定 00106 NULL // 小アイコン(なし) 00107 }; 00108 00109 // ウィンドウクラスの登録 00110 if (RegisterClassEx(&wcex) == 0) { 00111 MessageBox(0, L"ウィンドウクラスの登録に失敗しました", NULL, MB_OK); 00112 return 0; // 登録失敗 00113 } 00114 00115 // ウィンドウの作成 00116 std::wstring wtitle; 00117 Poco::UnicodeConverter::toUTF16(_conf.windowTitle, wtitle); 00118 if (_conf.fullsceen) { 00119 // フルスクリーン 00120 // 画面全体の幅と高さを取得 00121 int sw = GetSystemMetrics(SM_CXSCREEN); 00122 int sh = GetSystemMetrics(SM_CYSCREEN); 00123 00124 hWnd = CreateWindow( 00125 wcex.lpszClassName, // 登録されているクラス名 00126 wtitle.c_str(), // ウインドウ名 00127 WS_POPUP, // ウインドウスタイル(ポップアップウインドウを作成) 00128 0, // ウインドウの横方向の位置 00129 0, // ウインドウの縦方向の位置 00130 _conf.mainRect.right, // ウインドウの幅 00131 _conf.mainRect.bottom, // ウインドウの高さ 00132 NULL, // 親ウインドウのハンドル(省略) 00133 NULL, // メニューや子ウインドウのハンドル 00134 hInstance, // アプリケーションインスタンスのハンドル 00135 NULL // ウインドウの作成データ 00136 ); 00137 00138 } else { 00139 // ウィンドウモード 00140 DWORD dwStyle; 00141 if (_conf.frame) { 00142 dwStyle = WS_OVERLAPPEDWINDOW; 00143 } else { 00144 dwStyle = WS_POPUP; 00145 } 00146 hWnd = CreateWindow(clsName, 00147 wtitle.c_str(), 00148 dwStyle, 00149 CW_USEDEFAULT, CW_USEDEFAULT, 00150 CW_USEDEFAULT, CW_USEDEFAULT, 00151 NULL, NULL, hInstance, NULL); 00152 00153 if (hWnd) { 00154 // ウィンドウサイズを再設定する 00155 RECT rect; 00156 int ww, wh; 00157 int cw, ch; 00158 00159 // ウインドウ全体の横幅の幅を計算 00160 GetWindowRect(hWnd, &rect); // ウインドウ全体のサイズ取得 00161 ww = rect.right - rect.left; // ウインドウ全体の幅の横幅を計算 00162 wh = rect.bottom - rect.top; // ウインドウ全体の幅の縦幅を計算 00163 00164 // クライアント領域の外の幅を計算 00165 GetClientRect(hWnd, &rect); // クライアント部分のサイズの取得 00166 cw = rect.right - rect.left; // クライアント領域外の横幅を計算 00167 ch = rect.bottom - rect.top; // クライアント領域外の縦幅を計算 00168 00169 ww = ww - cw; // クライアント領域以外に必要な幅 00170 wh = wh - ch; // クライアント領域以外に必要な高さ 00171 00172 // ウィンドウサイズの再計算 00173 ww = _conf.mainRect.right + ww; // 必要なウインドウの幅 00174 wh = _conf.mainRect.bottom + wh; // 必要なウインドウの高さ 00175 00176 // ウインドウサイズの再設定 00177 SetWindowPos(hWnd, HWND_TOP, _conf.mainRect.left, _conf.mainRect.top, ww, wh, 0); 00178 00179 // ドラック&ドロップの受付 00180 DragAcceptFiles(hWnd, TRUE); 00181 } 00182 } 00183 if (!hWnd) { 00184 MessageBox(0, L"ウィンドウの生成に失敗しました", NULL, MB_OK); 00185 return 0; 00186 } 00187 00188 // ウィンドウの表示 00189 UpdateWindow(hWnd); 00190 ShowWindow(hWnd, nCmdShow); 00191 00192 // WM_PAINTが呼ばれないようにする 00193 ValidateRect(hWnd, 0); 00194 00195 // レンダラーの初期化 00196 _renderer = new Renderer(); 00197 HRESULT hr = _renderer->initialize(hInstance, hWnd); 00198 if (FAILED(hr)) { 00199 MessageBox(0, L"レンダラーの初期化に失敗しました", NULL, MB_OK); 00200 return 0; // 初期化失敗 00201 } 00202 00203 Poco::Net::HTTPStreamFactory::registerFactory(); 00204 Poco::Net::HTTPSStreamFactory::registerFactory(); 00205 Poco::SharedPtr<Poco::Net::PrivateKeyPassphraseHandler> console = new Poco::Net::KeyConsoleHandler(false); 00206 Poco::SharedPtr<Poco::Net::InvalidCertificateHandler> cert = new Poco::Net::ConsoleCertificateHandler(false); 00207 Poco::Net::Context::Ptr context = new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", Poco::Net::Context::VERIFY_NONE); 00208 Poco::Net::SSLManager::instance().initializeClient(console, cert, context); 00209 // init of server part is not required, but we keep the code here as an example 00210 /* 00211 ptrConsole = new KeyConsoleHandler(true); // ask the user via console for the pwd 00212 ptrCert = new ConsoleCertificateHandler(true); // ask the user via console 00213 ptrContext = new Context("any.pem", "rootcert.pem", true, Context::VERIFY_NONE, 9, false, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); 00214 SSLManager::instance().initializeServer(ptrConsole, ptrCert, ptrContext); 00215 */ 00216 00217 //_uim = new ui::UserInterfaceManager(*_renderer); 00218 //_uim->initialize(); 00219 // シーンの生成 00220 CaptureScenePtr captureScene = NULL; 00221 if (_conf.hasScene("capture")) { 00222 captureScene = new CaptureScene(*_renderer); 00223 captureScene->initialize(); 00224 _renderer->addScene("capture", captureScene); 00225 } 00226 MainScenePtr mainScene = NULL; 00227 if (true) { 00228 mainScene = new MainScene(*_renderer); 00229 _renderer->addScene("main", mainScene); 00230 } 00231 //if (_conf.hasScene("diff")) { 00232 // DiffDetectScenePtr diffScene = new DiffDetectScene(*_renderer); 00233 // diffScene->initialize(); 00234 // _renderer->addScene("diff", diffScene); 00235 //} 00236 // UserInterfaceScenePtr uiScene = new UserInterfaceScene(*_renderer, _uim); 00237 // _renderer->addScene("ui", uiScene); 00238 00239 Poco::Net::HTTPServerParams* params = new Poco::Net::HTTPServerParams; 00240 params->setMaxQueued(_conf.maxQueued); 00241 params->setMaxThreads(_conf.maxThreads); 00242 Poco::Net::ServerSocket socket(_conf.serverPort); 00243 Poco::Net::HTTPServer* server = new Poco::Net::HTTPServer(new SwitchRequestHandlerFactory(*_renderer), socket, params); 00244 server->start(); 00245 00246 // メッセージ処理および描画ループ 00247 //EmptyWorkingSet(GetCurrentProcess()); 00248 //::SetThreadAffinityMask(::GetCurrentThread(), 1); 00249 mainloop(hWnd); 00250 00251 log.information(Poco::format("shutdown web api server: %dthreads", server->currentThreads())); 00252 server->stop(); 00253 int t = 10; 00254 while (t-- > 0) { 00255 _renderer->peekMessage(); 00256 Poco::Thread::sleep(50); 00257 } 00258 SAFE_DELETE(server); 00259 Poco::Net::SSLManager::instance().shutdown(); 00260 00261 int exitCode = _renderer->getExitCode(); 00262 log.information(Poco::format("shutdown system (%d)", exitCode)); 00263 SAFE_DELETE(_renderer); 00264 //SAFE_DELETE(_uim); 00265 _conf.save(); 00266 _conf.release(); 00267 CoUninitialize(); 00268 00269 UnregisterClass(clsName, wcex.hInstance); 00270 return exitCode; 00271 } 00272 00273 void mainloop(HWND hWnd) { 00274 EXCEPTION_RECORD ERecord; 00275 CONTEXT EContext; 00276 __try { 00277 SetErrorMode(SEM_NOGPFAULTERRORBOX); 00278 00279 LARGE_INTEGER freq; 00280 LARGE_INTEGER start; 00281 LARGE_INTEGER current; 00282 ::QueryPerformanceFrequency(&freq); 00283 ::QueryPerformanceCounter(&start); 00284 LONGLONG last = 0; 00285 00286 // DWORD lastSwapout = 0; 00287 while (_renderer->peekMessage()) { 00288 // 処理するメッセージが無いときは描画を行う 00289 //if (_interruptFile.length() > 0) { 00290 // scene->prepareInterruptFile(_interruptFile); 00291 // _interruptFile.clear(); 00292 //} 00293 00294 ::QueryPerformanceCounter(¤t); 00295 LONGLONG time = (current.QuadPart - start.QuadPart) * 1000 / freq.QuadPart; 00296 last = time; 00297 00298 // ウィンドウが見えている時だけ描画するための処理 00299 WINDOWPLACEMENT wndpl; 00300 GetWindowPlacement(hWnd, &wndpl); // ウインドウの状態を取得 00301 bool visibled = (wndpl.showCmd != SW_HIDE) && (wndpl.showCmd != SW_MINIMIZE) && (wndpl.showCmd != SW_SHOWMINIMIZED) && (wndpl.showCmd != SW_SHOWMINNOACTIVE); 00302 _renderer->renderScene(visibled, time); 00303 Poco::Thread::sleep(_conf.frameIntervals); 00304 } 00305 } __except(ERecord = *(GetExceptionInformation())->ExceptionRecord, 00306 EContext = *(GetExceptionInformation())->ContextRecord, EXCEPTION_EXECUTE_HANDLER) { 00307 00308 FILE* fp; 00309 time_t gmt; 00310 time(&gmt); 00311 struct tm* localTime = localtime(&gmt); 00312 fp = fopen("exception_rec.txt", "a"); 00313 if (fp != NULL) { 00314 fprintf(fp, "----------------------¥n"); 00315 //fprintf(fp, "例外を検出しました¥n"); 00316 fprintf(fp, "例外発生日時:%.19s¥n", asctime(localTime)); 00317 fprintf(fp, "例外コード:%X¥n", ERecord.ExceptionCode); 00318 fprintf(fp, "例外フラグ:%d¥n", ERecord.ExceptionFlags); 00319 fprintf(fp, "例外発生アドレス:%X¥n", ERecord.ExceptionAddress); 00320 fclose(fp); 00321 } 00322 } 00323 } 00324 00325 LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 00326 { 00327 switch (msg) { 00328 case WM_CREATE: 00329 ::ShowCursor(config().mouse); 00330 break; 00331 00332 case WM_CLOSE: // ウインドウが閉じられた 00333 ::ShowCursor(TRUE); 00334 PostQuitMessage(0); // アプリケーションを終了する 00335 break; 00336 00337 case WM_SETFOCUS: 00338 break; 00339 case WM_KILLFOCUS: 00340 break; 00341 00342 case WM_IME_SETCONTEXT: 00343 lParam &= ‾ISC_SHOWUIALL; 00344 break; 00345 case WM_IME_STARTCOMPOSITION: 00346 case WM_IME_COMPOSITION: 00347 case WM_IME_ENDCOMPOSITION: 00348 return 0; 00349 case WM_IME_NOTIFY: 00350 switch(wParam){ 00351 case IMN_OPENSTATUSWINDOW: 00352 case IMN_CLOSESTATUSWINDOW: 00353 case IMN_OPENCANDIDATE: 00354 case IMN_CHANGECANDIDATE: 00355 case IMN_CLOSECANDIDATE: 00356 return 0; 00357 default: 00358 return DefWindowProc(hWnd, msg, wParam, lParam); 00359 } 00360 00361 00362 case WM_KEYDOWN: 00363 if (wParam == VK_ESCAPE) { 00364 PostQuitMessage(0); 00365 } else { 00366 bool shift = GetKeyState(VK_SHIFT) < 0; 00367 bool ctrl = GetKeyState(VK_CONTROL) < 0; 00368 if (_renderer) _renderer->notifyKeyDown(wParam, shift, ctrl); 00369 } 00370 break; 00371 case WM_KEYUP: 00372 { 00373 bool shift = GetKeyState(VK_SHIFT) < 0; 00374 bool ctrl = GetKeyState(VK_CONTROL) < 0; 00375 if (ctrl && wParam == 'S') { 00376 swapout(); 00377 } else { 00378 if (_renderer) _renderer->notifyKeyUp(wParam, shift, ctrl); 00379 } 00380 } 00381 break; 00382 00383 case WM_MOUSEMOVE: 00384 //if (_uim) _uim->notifyMouseMove(LOWORD(lParam), HIWORD(lParam)); 00385 break; 00386 case WM_MOUSEWHEEL: 00387 //if (_uim) _uim->notifyMouseWheel((SHORT)HIWORD(wParam)); 00388 break; 00389 case WM_LBUTTONDOWN: 00390 //if (_uim) _uim->notifyButtonDownL(LOWORD(lParam), HIWORD(lParam)); 00391 ::SetCapture(hWnd); 00392 break; 00393 case WM_LBUTTONUP: 00394 //if (_uim) _uim->notifyButtonUpL(LOWORD(lParam), HIWORD(lParam)); 00395 ::ReleaseCapture(); 00396 break; 00397 case WM_RBUTTONDOWN: 00398 //_uim->notifyButtonDownR(LOWORD(lParam), HIWORD(lParam)); 00399 ::SetCapture(hWnd); 00400 break; 00401 case WM_RBUTTONUP: 00402 //if (_uim) _uim->notifyButtonUpR(LOWORD(lParam), HIWORD(lParam)); 00403 ::ReleaseCapture(); 00404 break; 00405 00406 case WM_DROPFILES: 00407 { 00408 HDROP hDrop = (HDROP)wParam; /* HDROPを取得 */ 00409 vector<WCHAR> dropFile(255); 00410 DragQueryFile(hDrop, 0, &dropFile[0], 256); /* 最初のファイル名を取得 */ 00411 DragFinish(hDrop); /* ドロップの終了処理 */ 00412 //Poco::UnicodeConverter::toUTF8(&dropFile[0], _interruptFile); 00413 } 00414 break; 00415 00416 case WM_DEVICECHANGE: 00417 { 00418 switch (wParam) { 00419 case DBT_DEVICEARRIVAL: 00420 { 00421 DEV_BROADCAST_HDR* data = (DEV_BROADCAST_HDR*)lParam; 00422 if (data && data->dbch_devicetype == DBT_DEVTYP_VOLUME) { 00423 DEV_BROADCAST_VOLUME* extend = (DEV_BROADCAST_VOLUME*)data; 00424 if (extend && _renderer) _renderer->addDrive(extend->dbcv_unitmask); 00425 } 00426 } 00427 break; 00428 case DBT_DEVICEREMOVECOMPLETE: 00429 { 00430 DEV_BROADCAST_HDR* data = (DEV_BROADCAST_HDR*)lParam; 00431 if (data && data->dbch_devicetype == DBT_DEVTYP_VOLUME) { 00432 DEV_BROADCAST_VOLUME* extend = (DEV_BROADCAST_VOLUME*)data; 00433 if (extend && _renderer) _renderer->removeDrive(extend->dbcv_unitmask); 00434 } 00435 } 00436 break; 00437 case DBT_DEVNODES_CHANGED: 00438 break; 00439 } 00440 _renderer->deviceChanged(); 00441 } 00442 break; 00443 00444 default: 00445 return DefWindowProc(hWnd, msg, wParam, lParam); 00446 } 00447 return 0; 00448 } 00449 00450 00451 Configuration& config() { 00452 return _conf; 00453 } 00454 00455 void swapout() { 00456 Poco::Logger& log = Poco::Logger::get(""); 00457 log.information("*** exec memory swapout"); 00458 EmptyWorkingSet(GetCurrentProcess()); 00459 return; 00460 00461 DWORD idProcess[1024]; 00462 DWORD bsize = 0; 00463 /* プロセス識別子を取得する */ 00464 if (EnumProcesses(idProcess, sizeof(idProcess), &bsize) == FALSE) { 00465 log.warning("failed EnumProcesses()"); 00466 return; 00467 } 00468 log.information("*** exec memory swapout"); 00469 int proc_num = bsize / sizeof(DWORD); 00470 WCHAR name[1024]; 00471 for (int i = 0; i < proc_num; i++) { 00472 /* プロセスハンドルを取得する */ 00473 HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_SET_QUOTA | PROCESS_VM_READ, FALSE, idProcess[i]); 00474 if (handle != NULL) { 00475 bool swapouted = false; 00476 if (handle != GetCurrentProcess()) { 00477 // 自分以外のプロセスをスワップアウトする 00478 swapouted = EmptyWorkingSet(handle)==TRUE; 00479 } 00480 HMODULE module[1024]; 00481 if (EnumProcessModules(handle, module, sizeof(module), &bsize) != FALSE ) { 00482 /* プロセス名を取得する */ 00483 if (GetModuleBaseName(handle, module[0], name, sizeof(name)) > 0) { 00484 PROCESS_MEMORY_COUNTERS pmc = {0}; 00485 GetProcessMemoryInfo(handle, &pmc, sizeof(PROCESS_MEMORY_COUNTERS)); 00486 int mem = pmc.WorkingSetSize / 1024; 00487 string swapout = swapouted?"<swapouted>":""; 00488 string nameUTF8; 00489 Poco::UnicodeConverter::toUTF8(wstring(name), nameUTF8); 00490 log.information(Poco::format("process: %20s %05dkB%s", nameUTF8, mem, swapout)); 00491 } 00492 } 00493 CloseHandle(handle); 00494 } 00495 } 00496 }