Здравствуйте!
Что-то не очень понимаю, как разделять ресурс с операционной системой, ну и вообще, со сторонними сущностями, которыми не всегда я управляю.
Может, это вопрос скорее в "Архитектуру", но вопрос в тч и по плюсикам, по реализации.
Краткая справка, кто не знаком с 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, которые создала сама.
Или, может, как-то ещё можно разрулить проблему?
Здравствуйте, Marty, Вы писали:
M>значение, которое я передал, запоминается в системе и начинает ею использоваться, и в этот момент курсор нельзя удалять. В этом и есть проблема.
А зачем вообще его удалять пока прога не сдохла?
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, Marty, Вы писали:
M>запилил фабрику курсоров, которая хранит у себя все созданные курсоры, удаляет их при завершении аппы, а при повторных запросах на создание возвращает уже ранее созданное, а Cursor не управляет временем жизни объекта операционной системы HCURSOR.
Так и надо.
M> эти сервисы мне могут вернуть свой предыдущий сырой хэндл
Применительно к курсору это всего то помогашка, чтоб можно было вернуть старый курсор после того как переключился на песочные часы например на время операции.
В остальных случаях ты на возвращаемое значение можешь покласть болт а курсор всегда выставлять в зависимости от того, над каким контролом мыша находится.
M> который уже нельзя просто и однозначно ассоциировать с моим объектом.
Дык это и незачем
M> При этом, если сторонний сервис использует этот мой сырой хэндл, то мне нельзя ничего с ним делать.
Ну как нельзя... Можешь его даже грохнуть, ничего эдакого не произойдёт, система не рухнет.
M> Ну и отличать как-то, хэндлы были мной созданы, или их не надо освобождать.
Фабрика ж твоя является владельцем и потому знает всё то, что создано тобой и что будет тобой же освобождено. Всё остальное — не твоё, и париться не надо.
M> Я полагал, что есть для подобного что-то проработанное, а я не знаю.
Чот до сих пор не понятно в чём именно твоя проблема то
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Здравствуйте, Marty, Вы писали:
M>Простой кейс длительной блокирующей операции:
struct ScopeCursor
M>{
M> HCURSOR hcurDestroy;
M> HCURSOR hcurRestore;
M> ScopeCursor(HCURSOR hcNew) : hcurDestroy(hcNew), hcurRestore(SetCursor(hcNew)) {}
M> ~ScopeCursor() { SetCursor(hcurRestore); DestroyCursor(hcurDestroy); }
M>};
M>void longBlockingJob()
M>{
M> auto savedCursor = ScopeCursor(CreateStockCursor(IDC_WAIT));
M> // Долгая работа
M> // Тут курсор сам будет восстановлен
}
M>Тут всё нормально, нет никаких проблем.
Ну как сказать

Почему не так:
struct ScopeCursor // За синтаксис извиняюсь, могу в чём-то наврать
{
private:
const HCURSOR& hcurCurrent;
const HCURSOR& hcurPrevious;
public:
ScopeCursor(const HCURSOR& hCursor) : hcurCurrent(hCursor), hcurPrevious(::SetCursor(hcurCurrent)) /* RAII/SRP: ScopeCursor "жизнью" не владеет */ {}
~ScopeCursor() { ::SetCursor(hcurPrevious); }
// Иногда бывает полезно явно восстанавлявать "свой" курсор после передачи управления в другой код, который может курсор поменять
HCURSOR Restore() { return ::SetCursor(hcurCurrent); }
};
M>Есть вариант, когда класс Cursor не владеет HCURSOR, а всеми созданными HCURSOR владеет фабрика, их создающая, и она разрушает при своём разрушении только те HCURSOR, которые создала сама.
Кажется, это тот самый вариант.