//
//
//
#include <stdio.h>

#include <winsock2.h>

#include <comdef.h>
#include <taskschd.h>
# pragma comment(lib, "taskschd.lib")
# pragma comment(lib, "comsupp.lib")
# pragma comment(lib, "credui.lib")

#include <PowrProf.h>
# pragma comment(lib, "PowrProf.lib")

// #include "Resource.h" --
#define IDS_APP_TITLE           103

#define IDR_MAINFRAME           128
#define IDD_IPTD_R1_DIALOG  102
#define IDD_ABOUTBOX            103
#define IDM_ABOUT               104
#define IDM_EXIT                105
#define IDI_IPTD_R1         107
#define IDI_SMALL               108
#define IDC_IPTD_R1         109
#define IDC_MYICON              2
#ifndef IDC_STATIC
#define IDC_STATIC              -1
#endif
// 新しいオブジェクトの次の既定値
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NO_MFC                 130
#define _APS_NEXT_RESOURCE_VALUE    129
#define _APS_NEXT_COMMAND_VALUE     32771
#define _APS_NEXT_CONTROL_VALUE     1000
#define _APS_NEXT_SYMED_VALUE       110
#endif
#endif
// -- #include "Resource.h"

#define DBG_LEBEL 3
#include "Raym/Log.h"
#include "Raym/Raym.h"
#include "Raym/GlobalHook.h"

#if 0
LPCWSTR wakeTaskName_ = L"com.mac.rmitachi.misc.PowerManager.WakeTask";
#else
LPCWSTR wakeTaskName_ = L"com.gmail.tim.and.pom.misc.PowerManager.WakeTask";
#endif

#define WM_TRAYICONMESSAGE  (WM_USER + 1)
#define MAX_LOADSTRING      100
#define IDLE_DETECT_COUNT   60

ITaskService *createTaskService()
{
    //  ------------------------------------------------------
    //  Initialize COM.
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (FAILED(hr))
    {
        printf("CoInitializeEx failed: %x\n", hr);
        return NULL;
    }
/*
    //  Set general COM security levels.
    hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
                              RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
                              RPC_C_IMP_LEVEL_IMPERSONATE,
                              NULL, 0, NULL);
    if (FAILED(hr))
    {
        printf("CoInitializeSecurity failed: %x\n", hr);
        CoUninitialize();
        return NULL;
    }
*/
    //  ------------------------------------------------------
    //  Create an instance of the Task Service. 
    ITaskService *pService = NULL;
    hr = CoCreateInstance(CLSID_TaskScheduler, NULL,
                          CLSCTX_INPROC_SERVER, IID_ITaskService,
                          (void**)&pService);
    if (FAILED(hr))
    {
        printf("Failed to create an instance of ITaskService: %x\n", hr);
        CoUninitialize();
        return NULL;
    }

    //  Connect to the task service.
    hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
    if (FAILED(hr))
    {
        printf("ITaskService::Connect failed: %x\n", hr);
        pService->Release();
        CoUninitialize();
        return NULL;
    }
    return pService;
}

namespace Raym
{

static Application *theApp_ = NULL;

LRESULT CALLBACK __Application_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (theApp_ != NULL)
    {
        return theApp_->WndProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

LRESULT CALLBACK KeyboardProc(int code,       // フックコード
                              WPARAM wParam,  // 仮想キーコード
                              LPARAM lParam)  // キーストロークメッセージの情報
{
    if (theApp_ != NULL)
    {
        theApp_->resetCounter();
    }
    return 0;
}

LRESULT CALLBACK MouseProc(int code,       // フックコード
                           WPARAM wParam,  // メッセージ識別子
                           LPARAM lParam)  // マウスの座標
{
    // マウス移動以外
    if ((wParam != WM_MOUSEMOVE) && (theApp_ != NULL))
    {
        theApp_->resetCounter();
    }
    return 0;
}

Application::Application()
{
    DebugLog0("%s", __FUNCTION__);
}

Application::~Application()
{
    DebugLog0("%s", __FUNCTION__);
}

void InitializeMenuItem(HMENU hmenu, LPTSTR lpszItemName, int nId, HMENU hmenuSub)
{
    MENUITEMINFO mii;
    
    mii.cbSize = sizeof(MENUITEMINFO);
    mii.fMask  = MIIM_ID | MIIM_TYPE;
    mii.wID    = nId;

    if (lpszItemName != NULL)
    {
        mii.fType      = MFT_STRING;
        mii.dwTypeData = lpszItemName;
    }
    else
    {
        mii.fType = MFT_SEPARATOR;
    }

    if (hmenuSub != NULL)
    {
        mii.fMask   |= MIIM_SUBMENU;
        mii.hSubMenu = hmenuSub;
    }

    InsertMenuItem(hmenu, nId, FALSE, &mii);
}

Application *Application::init(HINSTANCE hInstance, int nCmdShow, LPCWSTR className)
{
    if (theApp_ != NULL)
    {
        return NULL;
    }

    theApp_ = this;
    _counter = 0;

    TCHAR szTitle[MAX_LOADSTRING];                  // タイトル バーのテキスト
    TCHAR szWindowClass[MAX_LOADSTRING];            // メイン ウィンドウ クラス名

    LoadString(hInstance, IDC_IPTD_R1, szWindowClass, MAX_LOADSTRING);
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);

    //
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = __Application_WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_IPTD_R1));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_IPTD_R1);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    RegisterClassEx(&wcex);

    _instance = hInstance;

    _wnd = CreateWindowEx(WS_EX_TRANSPARENT,    // ex windows style
                          szWindowClass,        // class name
                          szTitle,              // window name
                          WS_OVERLAPPEDWINDOW,  // window style
                          0,                    // x positon
                          0,                    // y position
                          CW_USEDEFAULT,        // width
                          CW_USEDEFAULT,        // height
                          HWND_DESKTOP,         // parent handle
                          NULL,                 // menu handle
                          hInstance,            // app handle
                          NULL);                // windows data


    if (!_wnd)
    {
        return NULL;
    }

    _menu = CreatePopupMenu();
    InitializeMenuItem(_menu, TEXT("終了"), IDM_EXIT, NULL);

    UpdateWindow(_wnd);

    if (!addNotifyIcon())
    {
        DestroyWindow(_wnd);
        return NULL;
    }

    // タイマ作成
    _timer = SetTimer(_wnd, 1, 1000, NULL);
    if (_timer == 0)
    {
        // 失敗
        DestroyWindow(_wnd);
        return NULL;
    }

    // グローバルフックを設定
    if (!SetGlobalHook(WH_KEYBOARD, KeyboardProc))
    {
        // 失敗
        KillTimer(_wnd, _timer);
        DestroyWindow(_wnd);
        return NULL;
    }
    if (!SetGlobalHook(WH_MOUSE, MouseProc))
    {
        // 失敗
        SetGlobalHook(WH_KEYBOARD, NULL);
        KillTimer(_wnd, _timer);
        DestroyWindow(_wnd);
        return NULL;
    }

    return this;
}

int Application::start()
{
    MSG msg;
    HACCEL hAccelTable;
    hAccelTable = LoadAccelerators(_instance, MAKEINTRESOURCE(IDC_IPTD_R1));

    // メイン メッセージ ループ:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    SetGlobalHook(WH_MOUSE, NULL);
    SetGlobalHook(WH_KEYBOARD, NULL);
    KillTimer(_wnd, _timer);

    return (int) msg.wParam;
}

LRESULT Application::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    static UINT taskbar_created;
    static BOOL idle_detected = false;

    switch (message)
    {
    case WM_CREATE:
        taskbar_created = RegisterWindowMessage(TEXT("TaskbarCreated"));
        break;

    case WM_TRAYICONMESSAGE:
        switch (lParam)
        {
        case WM_LBUTTONDOWN:
            break;
        case WM_RBUTTONDOWN:
            {
                POINT pt;
                GetCursorPos(&pt);
                SetForegroundWindow(_wnd);
                TrackPopupMenu(_menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, _wnd, NULL);
                PostMessage(_wnd, WM_NULL, NULL, NULL);
            }
            break;
        }
        break;

    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // 選択されたメニューの解析:
        switch (wmId)
        {
        case IDM_ABOUT:
//            DialogBox(_instance, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            if (canTerminate())
            {
                static bool flag = false;
                if (!flag)
                {
                    flag = true;
                    if (MessageBox(hWnd, TEXT("終了しますか？"), TEXT("確認"), MB_YESNO | MB_ICONQUESTION) == IDYES)
                    {
                        deleteNotifyIcon();
                        DestroyWindow(_wnd);

                        DebugLog0("will terminate");
                    }
                    flag = false;
                }
            }
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;

    case WM_DESTROY:
        deleteNotifyIcon();
        PostQuitMessage(0);
        break;

    case WM_POWERBROADCAST:
        switch (wParam)
        {
        case PBT_APMSUSPEND:
            systemWillSuspend();
            break;

        case PBT_APMRESUMEAUTOMATIC:
            systemResumed();
            break;
/*
        case PBT_APMRESUMESUSPEND:
            systemResumed();
            break;
*/
        default:
            break;
        }
        break;

    case WM_TIMER:
        if (idle_detected && (_counter == 0))
        {
            idle_detected = false;
            detectNonIdle();
        }
        ++_counter;
        if (_counter > IDLE_DETECT_COUNT)
        {
            idle_detected = true;
            detectIdle();
            _counter = 1;
        }

        // 念のためスリープ機能は抑止しておく
        SetThreadExecutionState(ES_DISPLAY_REQUIRED);
        break;

    default:
        if (message == taskbar_created)
        {
            addNotifyIcon();
        }
        break;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

void Application::resetCounter()
{
    _counter = 0;
}

void Application::sleep()
{
    _counter = 0;

    SetSuspendState(FALSE, FALSE, FALSE);
}

void Application::suspend()
{
    _counter = 0;

    HANDLE              hToken;     // アクセストークンのハンドル
    LUID                Luid;       // LUID(ローカル・ユニークID)
    TOKEN_PRIVILEGES    OldPriv;    // 特権トークン情報(変更前の特権)
    TOKEN_PRIVILEGES    NewPriv;    // 特権トークン情報(新しい特権)
    DWORD               dwSize;     // 特権トークン容量(変更前の特権)

    // アクセストークンのハンドルを取得
    if (OpenProcessToken(GetCurrentProcess(), (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY), &hToken))
    {
        if ( LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&Luid) ){    // 特権名のLUIDを取得
            NewPriv.PrivilegeCount              = 1;                // 特権数
            NewPriv.Privileges[0].Luid          = Luid;             // 識別子
            NewPriv.Privileges[0].Attributes    = SE_PRIVILEGE_ENABLED;
            
            // 特権トークン状態の有効/無効
            if ( AdjustTokenPrivileges(hToken,FALSE,&NewPriv,sizeof(TOKEN_PRIVILEGES),&OldPriv,&dwSize) ){
                if ( GetLastError() == ERROR_SUCCESS ){
//                    bSuccess = TRUE;
                }
            }
        }
        CloseHandle( hToken );
    }

    SetSystemPowerState(FALSE, FALSE);
}

void Application::shutdown()
{
    _counter = 0;

    system("shutdown /s");

    deleteNotifyIcon();
    DestroyWindow(_wnd);

    DebugLog0("will terminate");
}

bool Application::addNotifyIcon()
{
    NOTIFYICONDATA nid;
    ZeroMemory(&nid, sizeof(NOTIFYICONDATA));
    nid.cbSize = sizeof(NOTIFYICONDATA);
    nid.hWnd = _wnd;
    nid.uID = 1;
    nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
    LoadString(_instance, IDC_IPTD_R1, nid.szTip, MAX_LOADSTRING);
    nid.uCallbackMessage = WM_TRAYICONMESSAGE;
    nid.hIcon = LoadIcon(_instance, MAKEINTRESOURCE(IDI_IPTD_R1));

    while (true)
    {
        if (Shell_NotifyIcon(NIM_ADD, &nid))
        {
            break;
        }
        if (GetLastError() != ERROR_TIMEOUT)
        {
            return false;
        }
        Sleep(1000);
        if (Shell_NotifyIcon(NIM_MODIFY, &nid))
        {
            break;
        }
    }

    return true;
}

void Application::deleteNotifyIcon()
{
    NOTIFYICONDATA nid;
    ZeroMemory(&nid, sizeof(NOTIFYICONDATA));

    nid.cbSize = sizeof(NOTIFYICONDATA);
    nid.hWnd = _wnd;
    nid.uID = 0;
    nid.uFlags = 0;

    Shell_NotifyIcon(NIM_DELETE, &nid);
}

bool Application::setWakeSchedule(int year, int month, int day, int hour, int min)
{
//    DebugLog2("PowerManager::setWakeSchedule()");

    HRESULT hr;

    //  ------------------------------------------------------
    //  Create an instance of the Task Service. 
    ITaskService *pService = createTaskService();
    if (pService == NULL)
    {
//        DebugLog3("createTaskService() error.");
        CoUninitialize();
        return false;
    }

    //  ------------------------------------------------------
    //  Get the pointer to the root task folder.  This folder will hold the
    //  new task that is registered.
    ITaskFolder *pRootFolder = NULL;
    hr = pService->GetFolder(_bstr_t( L"\\") , &pRootFolder);
    if (FAILED(hr))
    {
//        DebugLog3("Cannot get Root folder pointer: %x", hr);
        pService->Release();
        CoUninitialize();
        return false;
    }
    
    //  Create the task definition object to create the task.
    ITaskDefinition *pTask = NULL;
    hr = pService->NewTask( 0, &pTask );

    pService->Release();  // COM clean up.  Pointer is no longer used.
    if (FAILED(hr))
    {
//        DebugLog3("Failed to CoCreate an instance of the TaskService class: %x", hr);
        pRootFolder->Release();
        CoUninitialize();
        return false;
    }

#if 0
    //  ------------------------------------------------------
    //  Get the registration info for setting the identification.
    IRegistrationInfo *pRegInfo= NULL;
    hr = pTask->get_RegistrationInfo( &pRegInfo );
    if( FAILED(hr) )
    {
        printf("Cannot get identification pointer: %x\n", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }

    hr = pRegInfo->put_Author( L"Author Name" );    
    pRegInfo->Release();  
    if( FAILED(hr) )
    {
        printf("Cannot put identification info: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }
#endif

    //  ------------------------------------------------------
    //  Create the principal for the task - these credentials
    //  are overwritten with the credentials passed to RegisterTaskDefinition
    IPrincipal *pPrincipal = NULL;
    hr = pTask->get_Principal( &pPrincipal );
    if( FAILED(hr) )
    {
//        DebugLog3("Can't get principal pointer: %x", hr);
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }
    
    //  Set up principal logon type to interactive logon
    hr = pPrincipal->put_LogonType( TASK_LOGON_INTERACTIVE_TOKEN );
    pPrincipal->Release();
    if( FAILED(hr) )
    {
//        DebugLog3("Can't put principal info: %x", hr);
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }  

    //  ------------------------------------------------------
    //  Create the settings for the task
    ITaskSettings *pSettings = NULL;
    hr = pTask->get_Settings( &pSettings );
    if( FAILED(hr) )
    {
//        DebugLog3("Can't get settings pointer: %x", hr);
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }
    
    //  Set setting values for the task.  
    hr = pSettings->put_StartWhenAvailable(VARIANT_TRUE);
    if( FAILED(hr) )
    {
//        DebugLog3("Can't put setting information: %x", hr);
        pSettings->Release();
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }

    hr = pSettings->put_WakeToRun(VARIANT_TRUE);
    if( FAILED(hr) )
    {
        printf("\nCannot put setting information: %x", hr );
        pSettings->Release();
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }

    // Set the idle settings for the task.
    IIdleSettings *pIdleSettings = NULL;
    hr = pSettings->get_IdleSettings( &pIdleSettings );
    pSettings->Release();
    if (FAILED(hr))
    {
//        DebugLog3("Can't get idle setting information: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }

    hr = pIdleSettings->put_WaitTimeout(L"PT5M");
    pIdleSettings->Release();
    if( FAILED(hr) )
    {
//        DebugLog3("Can't put idle setting information: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }
    

    //  ------------------------------------------------------
    //  Get the trigger collection to insert the time trigger.
    ITriggerCollection *pTriggerCollection = NULL;
    hr = pTask->get_Triggers( &pTriggerCollection );
    if( FAILED(hr) )
    {
//        DebugLog3("Can't get trigger collection: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }

    //  Add the time trigger to the task.
    ITrigger *pTrigger = NULL;    
    hr = pTriggerCollection->Create( TASK_TRIGGER_TIME, &pTrigger );     
    pTriggerCollection->Release();
    if( FAILED(hr) )
    {
//        DebugLog3("Can't create trigger: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }

    ITimeTrigger *pTimeTrigger = NULL;
    hr = pTrigger->QueryInterface( 
        IID_ITimeTrigger, (void**) &pTimeTrigger );
    pTrigger->Release();
    if( FAILED(hr) )
    {
//        DebugLog3("QueryInterface call failed for ITimeTrigger: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }

    hr = pTimeTrigger->put_Id( _bstr_t( L"Trigger1" ) );
    if (FAILED(hr))
    {
//        DebugLog3("Can't put trigger ID: %x", hr);
    }

    hr = pTimeTrigger->put_EndBoundary( _bstr_t(L"2034-05-02T08:00:00") );
    if (FAILED(hr))
    {
//        DebugLog3("Can't put end boundary on trigger: %x", hr);
    }

    char tmpstr[32];
    sprintf_s(tmpstr, "%04d-%02d-%02dT%02d:%02d:00", year, month, day, hour, min);
//    wstring aaaa = tmpstr;

    //  Set the task to start at a certain time. The time 
    //  format should be YYYY-MM-DDTHH:MM:SS(+-)(timezone).
    //  For example, the start boundary below
    //  is January 1st 2005 at 12:05
//    hr = pTimeTrigger->put_StartBoundary( _bstr_t(L"2005-01-01T12:05:00") );
    hr = pTimeTrigger->put_StartBoundary(_bstr_t(tmpstr));
    pTimeTrigger->Release();
    if( FAILED(hr) )
    {
//        DebugLog3("Can't add start boundary to trigger: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }
    

    //  ------------------------------------------------------
    //  Add an action to the task. This task will execute notepad.exe.     
    IActionCollection *pActionCollection = NULL;

    //  Get the task action collection pointer.
    hr = pTask->get_Actions( &pActionCollection );
    if( FAILED(hr) )
    {
//        DebugLog3("Can't get Task collection pointer: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }
    
    //  Create the action, specifying that it is an executable action.
    IAction *pAction = NULL;
    hr = pActionCollection->Create( TASK_ACTION_EXEC, &pAction );
    pActionCollection->Release();
    if( FAILED(hr) )
    {
//        DebugLog3("Can't create the action: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }

    IExecAction *pExecAction = NULL;
    //  QI for the executable task pointer.
    hr = pAction->QueryInterface( 
        IID_IExecAction, (void**) &pExecAction );
    pAction->Release();
    if( FAILED(hr) )
    {
//        DebugLog3("QueryInterface call failed for IExecAction: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }

    //  Get the windows directory and set the path to notepad.exe.
//    wstring wstrExecutablePath = _wgetenv( L"WINDIR");
//    wstrExecutablePath += L"\\SYSTEM32\\WSCRIPT.EXE";

    //  Set the path of the executable to notepad.exe.
//    hr = pExecAction->put_Path( _bstr_t( wstrExecutablePath.c_str() ) );
    hr = pExecAction->put_Path(_bstr_t(GetExecutePath()));
    if (FAILED(hr))
    {
//        DebugLog3("Can't put action path: %x", hr );
        pExecAction->Release();
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }  

    //  Set the path of the executable to notepad.exe.
    hr = pExecAction->put_Arguments(_bstr_t(L"resume"));
    pExecAction->Release();
    if( FAILED(hr) )
    {
//        DebugLog3("Can’t put action path: %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }  

    //  ------------------------------------------------------
    //  Save the task in the root folder.
    IRegisteredTask *pRegisteredTask = NULL;
    VARIANT varPassword;
    varPassword.vt = VT_EMPTY;
    hr = pRootFolder->RegisterTaskDefinition(_bstr_t(wakeTaskName_),
                                             pTask,
                                             TASK_CREATE_OR_UPDATE,
                                             _variant_t(L""),
//                                             _variant_t(L"Network Service"),
                                             varPassword,
                                             TASK_LOGON_INTERACTIVE_TOKEN,
                                             _variant_t(L""),
                                             &pRegisteredTask);
    if (FAILED(hr))
    {
//        DebugLog3("Error saving the Task : %x", hr );
        pRootFolder->Release();
        pTask->Release();
        CoUninitialize();
        return false;
    }
    
    //  Clean up.
    pRootFolder->Release();
    pTask->Release();
    pRegisteredTask->Release();
    CoUninitialize();

    return true;
}

void Application::resetWakeSchedule()
{
    ITaskService *pService = createTaskService();
    if (pService != NULL)
    {
        ITaskFolder *pRootFolder = NULL;
        HRESULT hr = pService->GetFolder(_bstr_t( L"\\") , &pRootFolder);
        if (!FAILED(hr))
        {
            pRootFolder->DeleteTask(_bstr_t(wakeTaskName_), 0);
            pRootFolder->Release();
        }
        pService->Release();
        CoUninitialize();
    }
}

//
// remark: This function is not thread safe.
//
const char *Application::GetHomeDirectory(void)
{
    static char home_directory_[MAX_PATH + 1];

    memset(home_directory_, 0x00, sizeof(home_directory_));

    TCHAR strbuf[MAX_PATH + 1];
    size_t len = GetEnvironmentVariable(L"USERPROFILE", strbuf, sizeof(strbuf));
    if (len > 0)
    {
        errno_t e;
        size_t returnValue;
        e = wcstombs_s(&returnValue, home_directory_, sizeof(home_directory_), strbuf, _TRUNCATE);
        if (e == 0)
        {
            return home_directory_;
        }
    }
    return NULL;
}

//
// remark: This function is not thread safe.
//
const char *Application::GetExecutePath(void)
{
    static char execute_path_[MAX_PATH + 1];
    memset(execute_path_, 0x00, sizeof(execute_path_));

    TCHAR strbuf[MAX_PATH + 1];
    if (GetModuleFileName(NULL, strbuf, MAX_PATH) != 0)
    {
        errno_t e;
        size_t returnValue;
        e = wcstombs_s(&returnValue, execute_path_, sizeof(execute_path_), strbuf, _TRUNCATE);
        if (e == 0)
        {
            return execute_path_;
        }
    }
    return NULL;
}

//
// remark: This function is not thread safe.
//
const char *Application::GetPublicDirectory(void)
{
    static char public_directory_[MAX_PATH + 1];

    memset(public_directory_, 0x00, sizeof(public_directory_));

    TCHAR strbuf[MAX_PATH + 1];
    size_t len = GetEnvironmentVariable(L"PUBLIC", strbuf, sizeof(strbuf));
    if (len > 0)
    {
        errno_t e;
        size_t returnValue;
        e = wcstombs_s(&returnValue, public_directory_, sizeof(public_directory_), strbuf, _TRUNCATE);
        if (e == 0)
        {
            return public_directory_;
        }
    }
    return NULL;
}

/*
 * Win32 アプリケーションのエントリポイント
 */
int Application::main(Application *(*allocator)(), LPCWSTR className, HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    int result = -1;

#ifdef RAYM_MEMORY_CHECK
    DebugLog0("");
    DebugLog0("Application::main() global_raym_count_ = %d", Raym::global_raym_count_);
#endif

    // ARP生成
    AutoreleasePool *pool = AutoreleasePool::alloc()->init();

    // winsockの初期化
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2,0), &wsaData);

    // アプリケーションインスタンス生成
    Application *app = allocator();
    if (app != NULL)
    {
        // アプリケーション初期化
        if (app->init(hInstance, nCmdShow, className))
        {
            // 実行
            result = app->start();
        }

        // アプリケーションインスタンス解放
        app->release();
    }

    // winsockのリソース解放
    WSACleanup();

    // ARP解放
    pool->release();

#ifdef RAYM_MEMORY_CHECK
    DebugLog0("Application::main() global_raym_count_ = %d", Raym::global_raym_count_);
#endif

    return result;
}


} // Raym

