Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Сервис с иконкой в трее


Автор: Разгильдяй 25.4.2008, 11:20
Есть сервис. К нему надо приделать управление. Добавил вывод иконки в трей, она выводится, меняет состояние при изменении состояния службы. Но не выводит меню на иконке. В режиме приложения все работает... 

Вот вырезки кода...

Код

...
    MyServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; // интерактивный
...
void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
    switch(Opcode) 
    {
    case SERVICE_CONTROL_STOP:
    case SERVICE_CONTROL_SHUTDOWN:
.....
        return;
    case SERVICE_CONTROL_CONTINUE:
        MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
        break;
.....
    };

    SetServiceStatus (MyServiceStatusHandle,  &MyServiceStatus);
    SetState();
}

HMENU GetMenu()
{
    HMENU hRes = NULL;
    mKey hKey;
    HKEY hMainKey = HKEY_LOCAL_MACHINE;
    switch (state)
    {
    case S_RUNNING:
        hRes = LoadMenu(hInstance, MAKEINTRESOURCE(1));
        HMENU hSub = GetSubMenu(hRes, 0);
...
        break;
    case S_STOPPED:
        hRes = LoadMenu(hInstance, MAKEINTRESOURCE(2));
        HMENU hSub = GetSubMenu(hRes, 0);
...
        break;
    }
    return hRes;
}

void SetState()
{
    int newState = state;
    if (scService || isService ){
        SC_HANDLE tmpSM;
        SC_HANDLE tmpSC = scService;
        if ( !scService )
        {
            tmpSM = scService ? scService : OpenSCManager(h.host, NULL, GENERIC_READ | GENERIC_EXECUTE);
            tmpSC = OpenService(tmpSM, SERVICE_NAME, GENERIC_EXECUTE | GENERIC_READ);
        }
        SERVICE_STATUS ss;
        if (QueryServiceStatus(tmpSC, &ss)){
            switch (ss.dwCurrentState)
            {
            case SERVICE_STOPPED:
                newState = S_STOPPED;
                break;
            case SERVICE_START_PENDING:
                newState = S_START_PENDING;
                break;
            case SERVICE_STOP_PENDING:
                newState = S_STOP_PENDING;
                break;
            case SERVICE_RUNNING:
                newState = S_RUNNING;
                break;
            }
        }
        if ( !scService )
        {
            CloseServiceHandle(tmpSC);
            CloseServiceHandle(tmpSM);
        }
    }else{
        newState = runState;
    }
    if (wantError && (newState == S_RUNNING)) wantError = FALSE;
    state = newState;
    if (_Shell_NotifyIcon){  // блок отрабатывает, проверял!!!
        ShowWindow(hWnd, SW_HIDE);
        NOTIFYICONDATA nf;
        nf.cbSize = sizeof(nf);
        nf.hWnd   = hWnd;
        nf.uID    = TRAYICON_ID;
        nf.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
        nf.uCallbackMessage = WM_USER;
        nf.hIcon = (state == S_RUNNING) ? IconRun : IconStop;
        char *s = StateText();
        lstrcpyn(nf.szTip, s, sizeof(nf.szTip));
        _Shell_NotifyIcon(NIM_MODIFY, &nf);
    }
    if (!fastTimer && ((state == S_START_PENDING) || (state == S_STOP_PENDING))){
        KillTimer(hWnd, 1);
        SetTimer(hWnd, 1, 100, NULL);
        fastTimer = TRUE;
    }
}

LRESULT CALLBACK WndProc(HWND hwnd,    UINT  uMsg,    WPARAM  wParam,    LPARAM  lParam)
{
    switch (uMsg)
    {
.....
    case WM_USER: 
        {
            UINT uID       = (UINT)wParam;
            UINT uMouseMsg = (UINT)lParam;
            if (uID != TRAYICON_ID) break;
            switch (uMouseMsg)
            {
            case WM_LBUTTONDBLCLK:
                if (hMenu){
                    DestroyMenu(hMenu);
                    hMenu = NULL;
                }
                hMenu = GetMenu();
                if (hMenu)
                    PostMessage(hwnd, WM_COMMAND, state ? ID_HOME : ID_START, 0);
                break;
            case WM_RBUTTONUP:
                {
                    if (hMenu){
                        DestroyMenu(hMenu);
                        hMenu = NULL;
                    }
                    hMenu = GetMenu();
                    if (hMenu){
                        SetActiveWindow(hwnd);
                        SetForegroundWindow(hwnd);
                        POINT pt;
                        GetCursorPos(&pt);
                        TrackPopupMenu(GetSubMenu(hMenu, 0), TPM_RIGHTBUTTON, pt.x,    pt.y,    0, hwnd, NULL);
                        PostMessage(hwnd, WM_USER, 0, 0);
                    }
                    break;
                }
            }
            return TRUE;
....
        }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void InitWindow()
{
    WNDCLASS wc;
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = 0;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = WND_CLASS;
    if (!RegisterClass(&wc)) return;

    hWnd = CreateWindow(WND_CLASS, WND_CLASS, WS_CHILD,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, GetDesktopWindow(), NULL, hInstance, NULL);
    if (hWnd == NULL) return;

         InitIcons();
        if (_Shell_NotifyIcon){
            ShowWindow(hWnd, SW_HIDE);
            NOTIFYICONDATA nf;
            nf.cbSize = sizeof(nf);
            nf.hWnd   = hWnd;
            nf.uID    = TRAYICON_ID;
            nf.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
            nf.uCallbackMessage = WM_USER;
            nf.hIcon = IconStop;
            char *s = StateText();
            lstrcpyn(nf.szTip, s, sizeof(nf.szTip));
            _Shell_NotifyIcon(NIM_ADD, &nf);
        }else{
            SetWindowText(hWnd, StateText());
            ShowWindow(hWnd, SW_SHOWMINNOACTIVE);
        }
}

int WINAPI WinMain(HINSTANCE  _hInstance, HINSTANCE, LPSTR cmdLine, int)
{
    hInstance = _hInstance;
    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
...
    SERVICE_TABLE_ENTRY srvTbl[2];
    srvTbl[0].lpServiceName = SERVICE_NAME;
    srvTbl[0].lpServiceProc = ServiceMain;
    srvTbl[1].lpServiceName = NULL;
    srvTbl[1].lpServiceProc = NULL;
...
    SC_HANDLE scManager = NULL;
    HINSTANCE hShell = LoadLibrary("SHELL32.DLL");
    if (hShell) *((FARPROC*)(&_Shell_NotifyIcon)) = GetProcAddress(hShell, "Shell_NotifyIconA");
    HINSTANCE hUser  = LoadLibrary("USER32.DLL");
    if (hUser) *((FARPROC*)(&_SetMenuDefaultItem)) = GetProcAddress(hUser, "SetMenuDefaultItem");
    InitWindow();
...
    if (checkService){
        isService = TRUE;
        if (StartServiceCtrlDispatcher(srvTbl)) return TRUE;
        int err = GetLastError() & 0xFFFF;
        if ((err != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) &&
            (err != ERROR_CALL_NOT_IMPLEMENTED)) return TRUE;
        isService = FALSE;
    }
    if (asService){
        scManager = OpenSCManager(h.host, NULL, GENERIC_READ | GENERIC_EXECUTE);
        if (scManager == NULL){
            if (GetLastError() == ERROR_ACCESS_DENIED){
                CloseWindow(hWnd);
                return FALSE;
            }
        }
    }
    if (scManager)
        scService = OpenService(scManager, SERVICE_NAME, GENERIC_EXECUTE | GENERIC_READ);
    
......
    return FALSE;
}



Скрытое окно создается, иконка меняется, меню не вызывается...  smile 
Может кто с этим сталкивался?
Подскажите.

Автор: Pulse69 25.4.2008, 15:17
Плохая идея - создавать элементы GUI прямо из сервиса. Лучше сделать дополнительное приложение, которое будет запрашивать у службы статус и, соответственно, каким-нибудь образом ей управлять.
Для того, чтобы GUI-элементы работали корректно, служба должна быть интерактивной (В свойствах службы на вкладке "Вход в систему" поставить переключатель на "С системной учётной записью" и поставить флажок "Разрешить взаимодействие с рабочим столом". Возможно, это не сделано, и возникают ошибки.

Автор: Rififi 25.4.2008, 18:07
Цитата(Pulse69 @  25.4.2008,  15:17 Найти цитируемый пост)
"Разрешить взаимодействие с рабочим столом"

В Висте этот способ уже отключили, так шта...

Автор: Разгильдяй 28.4.2008, 16:12
Цитата(Pulse69 @  25.4.2008,  15:17 Найти цитируемый пост)
Для того, чтобы GUI-элементы работали корректно, служба должна быть интерактивной (В свойствах службы на вкладке "Вход в систему" поставить переключатель на "С системной учётной записью" и поставить флажок "Разрешить взаимодействие с рабочим столом". Возможно, это не сделано, и возникают ошибки.


Все выполнено

Код

    MyServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; // интерактивный


Эта строка все делает....

Цитата(Rififi @  25.4.2008,  18:07 Найти цитируемый пост)
В Висте этот способ уже отключили, так шта... 


Специально проверил в Висте, ничего не отключили, в настройках все оК!!!!
И системная запись, и интерактивность присутствуют....

Автор: Rififi 29.4.2008, 11:17
http://msdn2.microsoft.com/en-us/library/ms683502.aspx
http://www.microsoft.com/whdc/system/vista/services.mspx

в Висте для обеспечения совместимости с кривыми сервисами написан специальный костыль.
если интересно, гуглить "Interactive Services Detection"

Автор: Разгильдяй 14.5.2008, 15:32
Сорри за задержку...
По поводу Висты я читал.
Но у меня и под XP popupmenu не работает.
Иконка появляется, в зависимости от состояния службы меняется, описание появляется, а меню по правой кнопке не вызывается.
Но ведь аналогично работает Windows Audio [AudioSrv]. По левой окошко регулятора громкости, по правой меню... Значит можно...
Но КАК????

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)