svvitch
digital signage player
D:/vs_workspace/switch_sf/src/svvitch/switch.cpp
Go to the documentation of this file.
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(&current);
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines