Вадим Смоленский

Пользователи
  • Публикаций

    192
  • Зарегистрирован

  • Посещение

  • Победитель дней

    5

Весь контент Вадим Смоленский

  1. Вадим Смоленский

    Постоянная потеря фокуса под Wine

    В феврале я сетовал, что мое Windows-приложение не хочет нормально запускаться в Linux под Wine 3.0. Но время идет, вышел Wine 3.6 - и теперь один из тестировщиков радостно сообщил, что всё заработало, за вычетом одного досадного момента. А именно: приложение способно сохранять фокус ввода лишь долю секунды, потом теряет. Соответственно, невозможно ничего ввести в текстовые боксы, разве только одну-две буквы. Можно щелкнуть по заголовку приложения, оно опять получит фокус - и через мгновение снова потеряет. Куда именно при этом переходит фокус, непонятно. Тестировщик утверждает, что только мое приложение ведет себя так, все остальные работают нормально. Нет ли у кого-нибудь идей? Что нужно проверить?
  2. Вадим Смоленский

    Уникальный идентификатор компьютера

    Узнал, что в UWP API включены средства для получения уникального идентификатора компьютера. Это юнит Windows.System.Profile, класс HardwareIdentification, метод getPackageSpecificToken. Существуют ли способы обратиться к этим средствам из Delphi? Вообще, я привык считать, что такая идентификация компьютера в принципе невозможна, в отличие от мобильных устройств. Но прогресс, как известно, не остановить.
  3. Вадим Смоленский

    [Windows] Сворачивание приложение

    Немножко улучшил свой вариант. Теперь вообще как часы всё заработало. var TheFormIsMinimized: Boolean = False; TheFormWasMaximized: Boolean = False; function NewWndProc(Wnd: HWND; Msg: UINT; WParam: WPARAM; LParam: LPARAM): LRESULT; stdcall; begin if (Msg = WM_SHOWWINDOW) and (LParam = SW_PARENTCLOSING) and (not TheFormIsMinimized) then begin TheFormIsMinimized := True; TheFormWasMaximized := (MainForm.WindowState=TWindowState.wsMaximized); SendMessage(FormToHWND(MainForm), WM_SYSCOMMAND, SC_MINIMIZE, 0); MainFormp.WindowState := TWindowState.wsMinimized; end else if (Msg = WM_SHOWWINDOW) and (LParam = SW_PARENTOPENING) and (TheFormIsMinimized) then begin TheFormIsMinimized := False; SendMessage(FormToHWND(MainForm), WM_SYSCOMMAND, SC_RESTORE, 0); if TheFormWasMaximized then MainForm.WindowState := TWindowState.wsMaximized else MainForm.WindowState := TWindowState.wsNormal; end else Result:=CallWindowProc(OldWndProc, Wnd, Msg, WParam, LParam); end; Единственный недостаток: при восстановлении из wsMinimized в wsMaximized на долю секунды окно фиксируется как wsNormal. Но это, в общем-то, не страшно.
  4. Год назад в одной из веток проскочила ссылка на китайский сайт (http://www.raysoftware.cn/?p=559), где предлагалось готовое решение для показа анимированных гифок средствами FireMonkey на любых платформах. Сейчас эта ссылка, как и весь сайт raysoftware.cn, упорно не открывается; по счастью, контент сохранился в гугловском кэше. Я скопировал оттуда код юнита и перевел гуглом все китайские комментарии на английский, добавив их в скобках. Там был еще сопроводительный текст, в котором автор излагал мотивы, побудившие его взяться за эту проблему; каких-то важных технических деталей я в этом тексте не увидел. Привожу весь юнит ниже и очень надеюсь, что кто-нибудь из продвинутых коллег объяснит мне, как именно этим кодом можно воспользоваться, чтобы показать пользователю анимированный файл в формате GIF. Моей программерской квалификации, к сожалению, не хватает.
  5. Вадим Смоленский

    [Windows] Сворачивание приложение

    Да, я сейчас тоже попробовал сделать через перехват, только попроще. Работает. Вот как выглядит функция: function NewWndProc(Wnd: HWND; Msg: UINT; WParam: WPARAM; LParam: LPARAM): LRESULT; stdcall; begin if (Msg = WM_SHOWWINDOW) and (LParam = SW_PARENTCLOSING) then begin SendMessage(FormToHWND(MainForm), WM_SYSCOMMAND, SC_MINIMIZE, 0); Application.ProcessMessages; MainForm.WindowState := TWindowState.wsMinimized; end else Result := CallWindowProc(OldWndProc, Wnd, Msg, WParam, LParam); end; ProcessMessages пришлось вставить из-за того, что без этого приложение перед сворачиванием на долю секунды становилось черным. Наверное, можно сделать изящнее - в вашем примере, как я понял, TThread помогает. Но все-таки оно работает, и это, безусловно, изящнее, чем курочить FMX.Platform.Win и таскать его туда-сюда.
  6. Вадим Смоленский

    [Windows] Сворачивание приложение

    Воспроизвел изменения у себя - и увы! Не только сворачивание не стало работать правильно, но даже перетащить окно теперь не получается. Что-то с этим кодом всё равно не так... Кстати, попробовал перехватить событие WM_SYSCOMMAND и обнаружил, что оно происходит только при щелчке по кнопке Minimize в шапке окна, а при щелчке по иконке в трее уже не происходит. Не то мы ловим.
  7. Вадим Смоленский

    Перехват сообщений в Windows

    Для задач, связанных с вводом японского текста, мне нужно научиться перехватывать системное сообщение WM_IME_NOTIFY. Насколько могу судить, в FireMonkey эти вещи делаются (если вообще делаются) принципиально иначе, нежели в VCL. Конкретного ничего не нагуглил. Не поможет ли кто?
  8. Вадим Смоленский

    Перехват сообщений в Windows

    Да! С таким булевым заграждением всё получилось! Видимо, у вас просто компьютер помощнее, процедура отрабатывает быстрее, чем вызывается по второму разу. Очень и очень признателен. Желаю всяческих благ - и отдельно успехов дочери на поприще японистики!
  9. Вадим Смоленский

    Перехват сообщений в Windows

    И что - прямо заводите японский текст, и выскакивает candidate window, и располагается на точке (100,100)?
  10. Вадим Смоленский

    Перехват сообщений в Windows

    Вынес всё, касающееся этой проблемы, в отдельный проект. У меня продолжает падать и в таком виде. Не поленитесь проверить, упадет ли у вас. Файл FMX.Platform.Win.pas я в свое время чуть изменил из-за каких-то проблем (сходу и не вспомню, каких), и с тех пор держу его в одной папке с проектом. Может, с ним что не так? ImeProject.zip
  11. Вадим Смоленский

    Перехват сообщений в Windows

    LookUp - это название моей формы, которое я забыл поменять на MyForm. Но можно и без него, конечно. Однако же, у меня и в таком виде всё равно валится. Что еще тут стоит проверить?..
  12. Вадим Смоленский

    Перехват сообщений в Windows

    Да, действительно. Теперь управление в нужные моменты передается WMIMENotify. Спасибо еще раз. Однако, к сожалению, это пока не помогло мне решить исходной проблемы. Не знаю, уместно ли будет изложить эту проблему здесь - все-таки она связана с процедурами IME для ввода японского текста; с этим мало кто сталкивается. И тем не менее: когда включена японская локаль и выбран ввод хираганой (слоговой азбукой), то при печатании в TEdit на экран автоматически выдается маленькое окошко (candidate window) со списком иероглифов или слов, которые могли бы соответствовать набранному чтению. Пользователь может выбрать одного из этих кандидатов. Проблема в том, что когда включено экранное масштабирование (скажем, 125%), candidate window выскакивает не строго под TEdit, как должно, а левее и выше. Похоже, это недоработка FireMonkey - при передаче координат курсора ввода масштабирование не учитывается. Я хотел наладить перехват выдачи candidate window с присвоением ему других координат. Сам перехват теперь получается (при вызове candidate window выдается сообщение WM_IME_NOTIFY с WParam=IMN_SETCANDIDATEPOS). А вот так выглядит у меня обработка этого перехвата в рекомендованной вами процедуре: uses imm; procedure TMyForm.WMIMENotify(var Msg: TMessage); var Imc: HIMC; ImeCandidateFormProperties: TCandidateForm; begin Imc:=ImmGetContext(FMXHandleToHWND(LookUp.Handle)); ImmGetCandidateWindow(Imc,0,@ImeCandidateFormProperties); ImeCandidateFormProperties.dwStyle:=CFS_CANDIDATEPOS; ImeCandidateFormProperties.ptCurrentPos.X:=100; ImeCandidateFormProperties.ptCurrentPos.Y:=100; ImmSetCandidateWindow(Imc,@ImeCandidateFormProperties); ImmReleaseContext(FMXHandleToHWND(LookUp.Handle),Imc); end; Сколь могу судить, ImmGetCandidateWindow успешно считывает данные о candidate window. Но заменить их на исправленные уже не выходит, при вызове ImmSetCandidateWindow происходит переполнение стека. Документация о ImmSetCandidateWindow лежит здесь. Опять же: трудно надеяться, что вы сможете помочь - но вдруг?
  13. Вадим Смоленский

    Перехват сообщений в Windows

    Да вот, kami говорит, что в FireMonkey всё по-другому...
  14. Вадим Смоленский

    Перехват сообщений в Windows

    Попробовал повторить у себя. Получилось странно. Во время запуска приложения управление множество раз переходит в NewWndProc, в том числе и дважды с сообщением WM_IME_NOTIFY. Но как только главное окно появилось на экране - больше ни разу, ни с какими сообщениями вообще. Что-то где-то недокручено, такое впечатление...
  15. Вадим Смоленский

    Перехват сообщений в Windows

    Можно считать, что в рамках окна. Спасибо, Некрасов-сан. Выглядит интригующе. Но я не могу понять, как управление перейдет в процедуру WMIMENotify. Она ведь у вас вообще нигде не вызывается. P.S. А, понял. Она вызывается в FMX.Platform.Win: WM_IME_NOTIFY: begin Result := WMImeNotify(LForm, hwnd, uMsg, wParam, lParam); end;
  16. Вадим Смоленский

    Windows.Services.Store namespace

    В многомесячной борьбе с Microsoft Store за размещение у них своего Windows-приложения я продвинулся настолько, что вплотную подошел к организации взимания денег с покупателей посредством механизма add-ons (in-app purchases). Но здесь меня ждала очередная засада. Необходимые программные инструменты, главным из которых считается класс StoreContext, лежат в пространстве имен Windows.Services.Store; файлы с такими именами (windows.services.store.h и windows.services.store.idl) в Windows 10 действительно находятся; но в Delphi я не могу найти юнита с чем-то подобным. Хотя на одном форуме видел утверждение годичной давности, что в релизе 10.2 Tokyo это уже есть. Не может ли кто-нибудь пролить свет? Не там ищу? Все-таки еще не добавили? Или, может, это появилось лишь в самой последней сборке 10.2.3? У меня стоит 10.2.2. Обещание написать подробную статью по итогам моих мытарств остается в силе. На этом пути столько подводных камней и неочевидных ходов, что грех о них не поведать. Но сначала нужно дойти до победного конца.
  17. Вадим Смоленский

    Windows.Services.Store namespace

    Спасибо. Вижу, что не у одного меня такая проблема. Библиотека по ссылке - это что-то дотнетовское, едва ли подойдет. Придется, видимо, подождать до релиза 10.3. Хотя вот еще нашелся интересный компонент. Платный, но стоит попробовать, думаю.
  18. Вадим Смоленский

    Обработка анимированных GIF по рецепту китайского коллеги

    Хорошо, если получится исправить. В моем-то проекте гифки самые простые, без прозрачности и черезстрочности, так что я ничего такого и не заметил.
  19. Вадим Смоленский

    Хук на клавиатуру

    В декабре я задавал здесь вопрос о борьбе с перехватами нажатий клавиш компонентом TWebBrowser. Продвинутый пользователь Kami посоветовал тогда, раз уж меня интересует только Windows, поставить хук на клавиатуру. Поделился полезной ссылкой. Добавил, что можно еще много чего нагуглить. Что-то действительно нагуглилось - но не в том объеме, чтобы я смог четко понять, как это следует делать. Вопросов много. Куда именно должна быть воткнута функция KeyboardProc? Что в ней должно содержаться, чтобы управление передавалось уже написанному обработчику события FormKeyDown? Многие также упоминают о возникающих проблемах с юникодом, и хорошо было бы понять, как уберечься от них. Буду очень признателен, если кто-нибудь осветит эту темную для меня материю.
  20. Вадим Смоленский

    Хук на клавиатуру

    Да нет, всё понятно теперь. Я просто упустил из вида, что внутри функции CallNextHookEx еще много всякого разного может происходить и ее выходное значение в данном случае - не самое главное. Кстати, в отладчике посмотрел - оно в штатном режиме действительно получается нулевым. Так что теперь в тех случаях, когда нажатие клавиши точно обработано моим приложением и я больше не хочу от него никаких сюрпризов, просто присваиваю Result:=1. Всё работает, как часы. Огромное спасибо!
  21. Вадим Смоленский

    Хук на клавиатуру

    Это непонятно. Какой именно <>0? Любой кроме ноля? А если, как советуют, присвоить CallNextHookEx(CurrentHook, nCode, wParam, lparam), то это всегда будет ноль, что ли?
  22. Вадим Смоленский

    Хук на клавиатуру

    Такого раздела там нет. Вы, видимо, имели в виду раздел "Return value". В частности, там сказано, что CallNextHookEx нужно вызывать в том случае, если (цитирую) the hook procedure did not process the message. То есть, насколько я понял, если моя процедура FormKeyDown установила Key:=0, то это можно трактовать в том ключе, что message обработан, и тогда CallNextHookEx вызывать необязательно. Так или не так? Ваши рекомендации меня запутали: строчку убирать не надо, я должен вызвать следующий хук, но лучше этот вызов пропустить. Как это всё трактовать в терминах конкретных действий? Я так и не смог понять разницы между WH_KEYBOARD_LL и WH_KEYBOARD. Мне казалось, что лишь первый вариант относится ко всей системе целиком и проверяется всеми приложениями, где есть хук, в то время как второй ограничен лишь одним-единственным приложением. Всё сложнее?
  23. Вадим Смоленский

    Хук на клавиатуру

    Вы имеете в виду, убрать эту строчку: Result := CallNextHookEx(CurrentHook, nCode, wParam, lparam); Я полагал, что "следующий хук" относится к возможному нажатию следующей клавиши, которое хранится в очереди. Разве это не так? И что присваивать результату в таком случае? Ноль?
  24. Вадим Смоленский

    Хук на клавиатуру

    Выявилась проблемка. Когда FormKeyDown вызывался как обработчик события, в самом его конце выполнялся оператор Key:=0. Это обнуляло нажатие клавиши, гарантировало, что оно не сработает каким-то дополнительным, непредвиденным образом. В новой конфигурации это не работает, я уже столкнулся с досадными побочными эффектами. Нажатие нужно обнулить в обработчике хука KeyboardProc. Но как?
  25. Вадим Смоленский

    Хук на клавиатуру

    Да, с виртуальным кодом клавиши проблем нет. Сложнее оказалось с параметром Shift - но нагуглилась страничка с хорошим примером, там я взял всё, что мне было нужно. Вот как теперь выглядит функция: function KeyboardProc(nCode: integer; wParam: integer; lParam: integer ): LongWord; stdcall; var W: Word; C: Char; KeyUp : Boolean; KeyState: TKeyboardState; TheShift: TShiftState; begin if (nCode < 0) then begin Result := CallNextHookEx(CurrentHook, nCode, wParam, lParam); Exit; end; KeyUp := ((lParam AND (1 shl 31)) <> 0); if not KeyUp then begin W:=wParam; C:=Chr(wParam); TheShift:=[]; if GetKeyboardState(KeyState) then begin if (lParam AND (1 shl 29))<>0 then TheShift:=[ssAlt]; if (GetKeyState(VK_CONTROL) AND (1 shl 15))<>0 then TheShift:=TheShift+[ssCtrl]; if (GetKeyState(VK_SHIFT) AND (1 shl 15))<>0 then TheShift:=TheShift+[ssShift]; end; Form1.FormKeyDown(Form1,W,C,TheShift); end; Result := CallNextHookEx(CurrentHook, nCode, wParam, lparam); end; Придирчиво еще не тестировал, но в первом приближении всё работает.