Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Как отловить нажатие VK_SNAPSHOT? [D7, 2000, XP, 2003]


antonn ©   (2010-11-14 00:46:00) 

В программе нужно поймать нажатие кнопки PrintScreen, ничего не подменять и прочего, работать только когда окно в фокусе (не "глобально"). Получить тот же функционал, что и в событиях OnKeyPress() - т.е. кнопка нажата и повторы нажатий (если они есть, как с буквами), т.е. GetAsyncKeyState() в каком нибудь таймере не подойдет.
Покопался в интернете, нашел раскопипащенный способ через хоткей ( http://www.delphimaster.ru/cgi-bin/faq.pl?look=1&id=988620866&n=15), но у меня этот способ работает "глобально" и при этом, видимо, "глушит" для остальных эту кнопку (по крайней мере в буфере все чисто, что уже не подходит). С хуками связываться не очень хочется... Реализация через другой раскопипащенный пример с обработкой в AppIdle() за одно быстрое нажатие выдает разом десяток "срабатываний" (ну там и GetAsyncKeyState()).
Есть ли какой проверенныйспособ узнать о нажатии этой кнопки, максимально независимый от всяких прав на действия (касперский спрашивает, хочу ли я позволить приложению установить ловушку :( ) и чтобы работало от win2k и выше?


antonn   (2010-11-15 18:01:00) [1]


> активно твое приложение, и получается твой скриншот = тому
> что снимет система.

нет, мой скриншот сохраненный мною - игровая сцена. Которая выводится в обычное окно, которое не максимизировано. Без кепшенов и прочего, а принтскрином делается снимок окна(только моего или рабочего стола - не важно).
И так же работает в какй либо другой вменяемой игрушке, если игра идет в окне, то принтскрин делает скриншот экрана (окна), а игра сохраняет только буфер вывода.


> а как оно внутри работает?

отслеживает клавишу и снимает снимок, переводит в палитру pf24bit и дальше сохраняет в заранее выбранном формате в выбраную папку. В буфере остается исходное изображение (иногда с модифицированной палитрой, вставку которого из буфера не переваривает ни один из моих редакторов (палитра 256 цветов, вставляется черный квадрат, это тоже игра, но старая), а вот эта программа позволяет снять без геморроя).


sniknik   (2010-11-15 17:42:00) [2]

> так некий "ScreenshotCaptor" аналогично делает скрины
а как оно внутри работает? ты что ли его писал? то что видно "сверху" аналогично, не значит что "внутрях" все по твоим понятиям сделано (вот лично я бы попытался минимизировать работу, и не делать двойных действий).

> Никто ничего не заменяет в буфере, потому что это не их дело что туда занесли и кто.
а тебе что то другое предлагали? тебе предлагали вставлять туда только когда действия совпадают. активно твое приложение, и получается твой скриншот = тому что снимет система.

хотя ладно, не хочешь понимать, твое дело.


antonn   (2010-11-15 15:20:00) [3]


> > не хочу вмешиваться в событие снятия скриншота.
> в своей программе ты однозначно вмешиваешься

Нет, не вмешиваюсь, я даю пользователю возможность "щелкать" игровые скриншоты, предлагая делать это по стандартной (для игр, по крайней мере) клавише. Но так же пользователь может привычно для себя "фотографировать" экранв котором присутствует мое окно, в том случае если мое окно не "полноэкранное". Повторюсь так же, что если пользователь захочет сфотографировать окно, то он щелкнет Alt+PrintScreen, у меня же сохраняется в файл не окно, а только игровой контент.
Игровой скриншот<>Alt+PrintScreen, потому что содержит только битмап игровой сцены, без кепшенов и бордюров формы.

Отсюда мне требовалось лишь узнать о том, что был нажата кнопка, без каких либо перехватов (ууу, за одно упоминание такого мне бы уже "набили морду"..) и переопределений. Переназначить кнопку снятия игрового скриншота я могу, оставалась проблема узнать о ее нажатии. И сабжевую кнопку в качестве "скриноснимателя" выбирает подавляющее большинство игроков.

Могу так же порекомендовать глянуть другие продукты похожей направленности, так некий "ScreenshotCaptor" аналогично делает скрины всего экрана (или части) сохраняя их сразу в файл, и при этом в буфере обмена остается то, что должно было снято кнопкой printscreen. Никто ничего не заменяет в буфере, потому что это не их дело что туда занесли и кто.


sniknik   (2010-11-15 13:02:00) [4]

> Мне лишь была нужна реакция на кнопку, отловить ее нажатие.
ты ее отлавливаешь в первом же -
> раскопипащенном способе через хоткей
сам же привел.

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

> а не моего окна
тогда ловить нужно Alt+PrintScreen, если окно.
по [3] аналог SimulateKeystroke(VK_SNAPSHOT, 1);

p.s. в общем проблема "высосана из пальца", в системе похоже специально ограничения на такой случай, типа "защиты от дурака", и делай либо правильно либо никак...


antonn   (2010-11-15 12:31:00) [5]


> раз уже сам "снял" скриншот, осталось только добавить его
> же в буфер обмена.

Нет, я не хочу вмешиваться в событие снятия скриншота. Юзер может сфотографировать какое нибудь AV (или банальный дефект от перерисовки на которое повлияла тема оформления, или вообще сделать снимок экрана, а не моего окна), а я ему буду подсовывать свой буфер? Тут есть один товарищ - он ведь и "морду набить" может за такое :)
Мне лишь была нужна реакция на кнопку, отловить ее нажатие.
Если бы я хотел, чтобы в буфер обмена помещался мой скриншот - я бы сделал поддержку Ctrl+C в своем окне.

Хук отлавливается всякими касперскими, я выше написал почему это может быть плохим методом. К тому же юзер может запретить один раз в этом самом касперском, а потом обратно разрешить задача не тривиальная для домашнего пользователя (речь конкретно об КИС8/9/10). А скрины сохраненные на диск сразу в png без не_клиентской части окна - удобно.


sniknik   (2010-11-15 09:41:00) [6]

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


antonn   (2010-11-15 09:02:00) [7]


> и потом скрин как раз там и делается, в нажатии, это тебе
> предлагается ловить отжатие чтобы узнать, что он сделался.
> ..
> или опять изменение планов? теперь ты уже ловишь чтобы делать
> скрин сам? тогда к чему тебе срабатывание системного? оно
> в первоначальном варианте как раз переопределяет нажатие
> и считает, что  на этом все скрин ты сделаешь и не нужно
> системный вызывать чтобы не перетереть твое.
>

Моя программа сохраняет в папку со скриншотами свой битмап, использование для этого системной кнопки лишь дань удобству и привычкам пользователям. Игра это


sniknik   (2010-11-15 07:55:00) [8]

>  почему в событии срабатывает, а в WndProc нет?
ошибка в твоей WndProc? почему бы не посмотреть как в vcl реализовано для того же события из формы.

> И по моему разумнее делать скрин в момент нажатия, проще ловить момент, чем нажать и ждать отжатия :(
а по моему разумнее делать так как работает в соответствии с системой.

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


antonn   (2010-11-15 00:51:00) [9]


> зачем усложнять (+ возможные ошибки вставлять), просто в
> событии формы достаточно.

она все равно перекрыта, но почему в событии срабатывает, а в WndProc нет?

И по моему разумнее делать скрин в момент нажатия, проще ловить момент, чем нажать и ждать отжатия :(


sniknik   (2010-11-15 00:14:00) [10]

> вот идиот, не в FormKeyDown ловлю =)))
надо в FormKeyUp, даун занят принтскрином.

> WndProc
зачем усложнять (+ возможные ошибки вставлять), просто в событии формы достаточно.


antonn   (2010-11-14 22:15:00) [11]

но все же хреново работает, на форме без контролов ввода (пара кнопок) опять же не отлавливается


antonn   (2010-11-14 22:13:00) [12]

вот идиот, не в FormKeyDown ловлю =)))


antonn   (2010-11-14 22:08:00) [13]


> sniknik ©   (14.11.10 21:29) [5]
>
> > предложили в оконной проедуре ловить
> стоп... ты же в [0] условие поставил, что не в окне(/форме).
> .. нет?

в данном случае форма есть


> кажется будет к месту

Нет, не к месту :)
Мне бы хотелось отлавливать принтскрин, будь хоть оконное приложение, хоть безоконное, иметь универсальный пример. Ну и как бы хотелось чтобы этому примеру было все равно есть ли окно в приложении или нет. Если это невозможно, и реализации с окном и без него "не пересекаются", то я думаю отвечающие (не только в этой теме) меня бы предупредили и давали советы "для окна" и "без него".
На данный моменту моего приложения есть окно, если это имело значение - надо было уточнить, извиняюсь :) Однако я же не зря привел ссылку на фак, там оконное приложение. Начал с малого :)

KeyPreview? Разумеется поставил св-во в true (в инспекторе, ну и в создании формы повторил, для проверки), но не доходит... Вообще ничего
type
 TForm1 = class(TForm)
   SpeedButton1: TSpeedButton;
   Button1: TButton;
   Memo1: TMemo;
   procedure FormCreate(Sender: TObject);
   procedure WndProc(var Message: TMessage); override;
 private
   { Private declarations }
 public
   { Public declarations }
 end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 KeyPreview:=true;
end;

procedure TForm1.WndProc(var Message: TMessage);
begin
 if(Message.msg=WM_KEYUP)and(Message.wParam=VK_SNAPSHOT) then begin
    memo1.Lines.Add(inttostr(random(65535)));
    caption:=inttostr(random(65535));
 end else
 if(Message.msg=WM_KEYUP)and(Message.wParam=VK_LEFT) then begin
    memo1.Lines.Add(inttostr(random(65535)));
    caption:=inttostr(random(65535));
 end;
 inherited WndProc(Message);
end;


sniknik   (2010-11-14 21:38:00) [14]

> используя шаманский бубен, миелофон, шестое чувство, телепатор и прочие БС (Божественные Силы)
KeyPreview?


sniknik   (2010-11-14 21:37:00) [15]

кажется будет к месту
http://www.gunsmoker.ru/2008/10/x-y-z.html


sniknik   (2010-11-14 21:29:00) [16]

> предложили в оконной проедуре ловить
стоп... ты же в [0] условие поставил, что не в окне(/форме)... нет?

> он ловится до тех пор, пока на форме нет других оконных компонент
в смысле? это уже на 17ю строку смахивает... подробнее нельзя?


antonn   (2010-11-14 20:47:00) [17]

Волшебный костыль :))
Да, была идея самому делать "принтскрин" в это время, но постеснялся такое воротить :)

На другом форуме предложили в оконной проедуре ловить, он ловится до тех пор, пока на форме нет других оконных компонент :( Ну да ладно, конкретно для своей задачи я поудалял все компоненты, но хотелось универсального красивого решения :(


sniknik   (2010-11-14 18:20:00) [18]

> А тот что в ФАКе не вставляет в буфер скриншот если хоткей повесить на окно? Хотелось бы знать это так и должно быть или у меня "что-то глючит" :)
у меня не вставляет, т.что ... не знаю. инхеритед не помогает, результ другой возвращать тоже, только то что выше, или "в лоб", как ниже.

procedure SimulateKeystroke(Key : byte; extra : DWORD);
begin
 keybd_event(Key, extra, 0, 0);
 keybd_event(Key, extra, KEYEVENTF_KEYUP, 0);
end;

procedure TForm1.WMHotKey (var Msg: TWMHotKey);
begin
 if Msg.HotKey = id_SnapShot then begin
   ShowMessage('GotIt');

   UnRegisterHotKey(Handle, id_SnapShot);
   try
      SimulateKeystroke(VK_SNAPSHOT, 0);
   finally
     RegisterHotKey(Handle, id_SnapShot, 0, VK_SNAPSHOT);
   end;
 end;
end;


antonn   (2010-11-14 14:20:00) [19]

А тот что в ФАКе не вставляет в буфер скриншот если хоткей повесить на окно? Хотелось бы знать это так и должно быть или у меня "что-то глючит" :)

С хуком касперский задает вопрос о допустимости установки ловушки. У меня программа может отправлять данные в интернет, и если такие сообщения будут вылезать - подозрительно будет выглядеть. Точнее я бы, если бы не знал что делает программа, не стал ей это разрешать :)


sniknik   (2010-11-14 10:21:00) [20]

> Есть ли какой проверенный способ
ну, не скажу что это очень правильно, и точно не проверено (пару раз мной, на одной машине, не считается)  
но исправил немного этот
> нашел раскопипащенный способ через хоткей

var
 HookHandle: hHook = 0;

function WMHotKey(Code: integer; wParam: WPARAM; lParam: LPARAM): LongInt; stdcall;
begin
 if Code >= 0 then begin
   if wParam = VK_SNAPSHOT then ShowMessage('GotIt');
   result:= 0;
 end else
   result:= CallNextHookEx(HookHandle, Code, wParam, lParam);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 //RegisterHotKey(Handle, id_SnapShot, 0, VK_SNAPSHOT);
 HookHandle:= SetWindowsHookEx(wh_Keyboard, @WMHotKey, hInstance, 0);
 if HookHandle = 0 then
   ShowMessage('Ошибка установки хука');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 //UnRegisterHotKey(Handle, id_SnapShot);
 if HookHandle <> 0 then
   UnhookWindowsHookEx(HookHandle);
end;


> при этом, видимо, "глушит"
этот, у меня (на моей машине - xp) не глушит, но что будет в win 7 не знаю.


Наверх

Титульная страница Поиск, карта сайта Написать письмо