Информация об изменениях

Сообщение Проблема HCURSOR, или как делить свой ресурс с другими? от 08.03.2024 17:51

Изменено 08.03.2024 17:51 Marty

Проблема HCURSOR, или как делить не свой ресурс с другими?
Здравствуйте!

Что-то не очень понимаю, как разделять ресурс с операционной системой, ну и вообще, со сторонними сущностями, которыми не всегда я управляю.

Может, это вопрос скорее в "Архитектуру", но вопрос в тч и по плюсикам, по реализации.

Краткая справка, кто не знаком с WinAPI.
WinAPI предоставляет следующие функции:
BOOL DestroyCursor(HCURSOR hCursor);
HCURSOR CreateCursor(...); // Создаёт курсор из битиков
HCURSOR LoadCursor(HINSTANCE hInstance, LPCSTR lpCursorName); // Загружает курсор из ресурсов, или из файла, или стоковый
HCURSOR SetCursor(HCURSOR hCursor); // Устанавливает текущий курсор и возвращает предыдущий


Тут проблема с SetCursor — она передаёт владение над HCURSOR системе, и возвращает предыдущий установленный хэндл, прекращая владение над ним. Тут ещё возможно важный нюанс, что HCURSOR это глобальный системный ресурс, т.е. если я в приложении установлю свой HCURSOR, то он вероятно останется в системе даже после гибели приложения. Я не проверял, но, судя по тому, что там не требуется хэндл окна, то хэндлом курсора либо приложение владеет, либо сама система.

Простой кейс длительной блокирующей операции:
struct ScopeCursor
{
    HCURSOR hcurDestroy;
    HCURSOR hcurRestore;
    ScopeCursor(HCURSOR hcNew) : hcurDestroy(hcNew), hcurRestore(SetCursor(hcNew)) {}
    ~ScopeCursor() { SetCursor(hcurRestore); DestroyCursor(hcurDestroy); }
};

void longBlockingJob()
{
    auto savedCursor = ScopeCursor(CreateStockCursor(IDC_WAIT));
    // Долгая работа
    // Тут курсор сам будет восстановлен
}


Тут всё нормально, нет никаких проблем.

Другой вариант — я хочу создать несколько курсоров заранее при создании окна, они грузятся из файлов и на каждый чих мне дорого их грузить, в процессе работы хочу произвольно их устанавливать, но когда окно разрушается и/или приложение завершается, я хочу установить тот курсор, который был до меня, и удалить то, что создал.

Тут для автоматического управления загруженными курсорами я начинаю управлять временем жизни HCURSOR, но проблема в том, что я уже не знаю, какой HCURSOR сейчас выбран.

Предположим, у меня есть объект курсора, и функция создания таких курсоров типа такого:
class Cursor
{
    HCURSOR hCursor;
    Cursor(HCURSOR hc) : hCursor(hc) {}
    // всякие copy/move ctor/op=
    ~Cursor()
    {
        ::DestroyCursor(hCursor);
    }
};

// Где-то в классе окна, или может, глобальная ф-я
Cursor createStockCursor(int id)
{
    return Cursor(LoadCursor(0, id));
}

void setDefaultCursor(); // установка дефолтного, см ниже

void setCursor(Cursor c);


Я создаю такие курсоры, они у меня где-то лежат, по мере необходимости я устанавливаю нужный, или восстанавливаю дефолтный.

Дефолтный курсор — можно при старте приложения сделать так для его получения:
class CMainWindow : // ...
{
    HCURSOR hCursorDefault;
// ...

    void OnCreate()
    {
        HCURSOR hTmpCursor = ::LoadCursor(NULL, IDC_WAIT);
        hCursorDefault     = ::SetCursor(hTmpCursor);
        ::SetCursor(hDefaultCursor); // Восстановили оригинальный курсор, но хэндл у нас уже есть
        ::DestroyCursor(hTmpCursor);
    }
};


При завершении на OnClose просто восстановить дефолтный:
    void OnCreate()
    {
        setDefaultCursor(); // ::SetCursor(hDefaultCursor);
        // Больше ничего не делаем, просто установили тот курсор, который был до нашего вмешательства, значит, все наши курсоры точно не заняты системой и их деструкторы отработают нормально
    }


Я могу написать в доке, что курсоры надо хранить в какой-то переменной, но доки никто не читает, и могут сделать тупо так:
setCursor(createStockCursor())


Т.е. тупо передать временный объект, и тогда, после установки курсора объект разрушится, и HCURSOR, которым он управлял, тоже будет уничтожен. Не уверен, что система у HCURSOR ведёт счетчик какой-то, и тогда окажется, что система владеет разрушенным HCURSOR. Не понятно, как быть в такой ситуации?

Ещё проблема в том, что описанный сценарий не позволяет восстановить не дефолтный курсор, а тот курсор, который был установлен ранее.

В общем, не очень понятно, как такую ситуацию разрулить?

Есть вариант, когда класс Cursor не владеет HCURSOR, а всеми созданными HCURSOR владеет фабрика, их создающая, и она разрушает при своём разрушении только те HCURSOR, которые создала сама.

Или, может, как-то ещё можно разрулить проблему?
Проблема HCURSOR, или как делить свой ресурс с другими?
Здравствуйте!

Что-то не очень понимаю, как разделять ресурс с операционной системой, ну и вообще, со сторонними сущностями, которыми не всегда я управляю.

Может, это вопрос скорее в "Архитектуру", но вопрос в тч и по плюсикам, по реализации.

Краткая справка, кто не знаком с WinAPI.
WinAPI предоставляет следующие функции:
BOOL DestroyCursor(HCURSOR hCursor);
HCURSOR CreateCursor(...); // Создаёт курсор из битиков
HCURSOR LoadCursor(HINSTANCE hInstance, LPCSTR lpCursorName); // Загружает курсор из ресурсов, или из файла, или стоковый
HCURSOR SetCursor(HCURSOR hCursor); // Устанавливает текущий курсор и возвращает предыдущий


Тут проблема с SetCursor — она передаёт владение над HCURSOR системе, и возвращает предыдущий установленный хэндл, прекращая владение над ним. Тут ещё возможно важный нюанс, что HCURSOR это глобальный системный ресурс, т.е. если я в приложении установлю свой HCURSOR, то он вероятно останется в системе даже после гибели приложения. Я не проверял, но, судя по тому, что там не требуется хэндл окна, то хэндлом курсора либо приложение владеет, либо сама система.

Простой кейс длительной блокирующей операции:
struct ScopeCursor
{
    HCURSOR hcurDestroy;
    HCURSOR hcurRestore;
    ScopeCursor(HCURSOR hcNew) : hcurDestroy(hcNew), hcurRestore(SetCursor(hcNew)) {}
    ~ScopeCursor() { SetCursor(hcurRestore); DestroyCursor(hcurDestroy); }
};

void longBlockingJob()
{
    auto savedCursor = ScopeCursor(CreateStockCursor(IDC_WAIT));
    // Долгая работа
    // Тут курсор сам будет восстановлен
}


Тут всё нормально, нет никаких проблем.

Другой вариант — я хочу создать несколько курсоров заранее при создании окна, они грузятся из файлов и на каждый чих мне дорого их грузить, в процессе работы хочу произвольно их устанавливать, но когда окно разрушается и/или приложение завершается, я хочу установить тот курсор, который был до меня, и удалить то, что создал.

Тут для автоматического управления загруженными курсорами я начинаю управлять временем жизни HCURSOR, но проблема в том, что я уже не знаю, какой HCURSOR сейчас выбран.

Предположим, у меня есть объект курсора, и функция создания таких курсоров типа такого:
class Cursor
{
    HCURSOR hCursor;
    Cursor(HCURSOR hc) : hCursor(hc) {}
    // всякие copy/move ctor/op=
    ~Cursor()
    {
        ::DestroyCursor(hCursor);
    }
};

// Где-то в классе окна, или может, глобальная ф-я
Cursor createStockCursor(int id)
{
    return Cursor(LoadCursor(0, id));
}

void setDefaultCursor(); // установка дефолтного, см ниже

void setCursor(Cursor c);


Я создаю такие курсоры, они у меня где-то лежат, по мере необходимости я устанавливаю нужный, или восстанавливаю дефолтный.

Дефолтный курсор — можно при старте приложения сделать так для его получения:
class CMainWindow : // ...
{
    HCURSOR hCursorDefault;
// ...

    void OnCreate()
    {
        HCURSOR hTmpCursor = ::LoadCursor(NULL, IDC_WAIT);
        hCursorDefault     = ::SetCursor(hTmpCursor);
        ::SetCursor(hDefaultCursor); // Восстановили оригинальный курсор, но хэндл у нас уже есть
        ::DestroyCursor(hTmpCursor);
    }
};


При завершении на OnClose просто восстановить дефолтный:
    void OnCreate()
    {
        setDefaultCursor(); // ::SetCursor(hDefaultCursor);
        // Больше ничего не делаем, просто установили тот курсор, который был до нашего вмешательства, значит, все наши курсоры точно не заняты системой и их деструкторы отработают нормально
    }


Я могу написать в доке, что курсоры надо хранить в какой-то переменной, но доки никто не читает, и могут сделать тупо так:
setCursor(createStockCursor())


Т.е. тупо передать временный объект, и тогда, после установки курсора объект разрушится, и HCURSOR, которым он управлял, тоже будет уничтожен. Не уверен, что система у HCURSOR ведёт счетчик какой-то, и тогда окажется, что система владеет разрушенным HCURSOR. Не понятно, как быть в такой ситуации?

Ещё проблема в том, что описанный сценарий не позволяет восстановить не дефолтный курсор, а тот курсор, который был установлен ранее.

В общем, не очень понятно, как такую ситуацию разрулить?

Есть вариант, когда класс Cursor не владеет HCURSOR, а всеми созданными HCURSOR владеет фабрика, их создающая, и она разрушает при своём разрушении только те HCURSOR, которые создала сама.

Или, может, как-то ещё можно разрулить проблему?