Почти SendKeys
Мне необходимо из Экселя вызвать в другом приложении контекстное меню. Обычный SendKeys не подходит, потому что вспомогательных виндузовых клавиш в нем нет. Меня устроит и WinAPI. Кто может помочь?
Честно говоря. помочь толком не смогу. Знаю только, что для этого, если сторонняя программа оконная, можно попробовать получить её дескриптор, а потом уж орудовать с ним. К сожалению, я в этом слаб. Спросите в форуме по API - там, 100%, должны знать. По крайней мере, заню, что это возможно.
Для вызова контексного меню, имитируем нажатие прав. клавиши мыши, через посылки сообщения ф. PostMessage. Для нее надо дескриптор окна приложения, ну и сами размеры окна нам тоже хорошобы знать, чтобы знать координаты куда тыкать то (а то мимо окна тыкнем), ну и оно еще фокус должно иметь конечно...
Поэтому задача номер один - найти это окно. Искть можно по заголовку окна, по уникальному классу окна, по имени exe файла (этот вариант посложнее, надо самому писать функцию перебора всех главных окно, с получением идент. процесса и проверки уже по нему запустившего его exe файла).
Мне необходимо из Экселя вызвать в другом приложении контекстное меню. Обычный SendKeys не подходит, потому что вспомогательных виндузовых клавиш в нем нет. Меня устроит и WinAPI. Кто может помочь?
в крайнем случае можно использовать функцию keybd_event. Наверное с параметром VK_LMENU или VK_RMENU
в крайнем случае можно использовать функцию keybd_event. Наверное с параметром VK_LMENU или VK_RMENU
Да тоже неплохой вариант, надо токо чтобы в системе была установлена расширенная клавиатура с этими доп. кнопками - но это почти все современные клавиатуры, так что особых проблем быть не должно, если у тебя только не особый какой-нибудь случай.
Да, есть еще вариант вызова контескного меню по его идентификатору (можно показать в его любом месте, сам будешь руководить), вот только я не в курсе как до него добраться, до обычного меню знаю, а вот до контексного... надо поискать, вопросик интересный.
Пишу в данном случае для себя. Поэтому и клава у меня расширенная, и даже окошко-адресат меня не напряжет в нужном месте расположить.
А дескриптор окна - это часом не PID в TaskManager? Имя окна я, конечно, знаю. По крайней мере, его начало. Что делать дальше?
Пишу в данном случае для себя. Поэтому и клава у меня расширенная, и даже окошко-адресат меня не напряжет в нужном месте расположить.
А дескриптор окна - это часом не PID в TaskManager? Имя окна я, конечно, знаю. По крайней мере, его начало. Что делать дальше?
дескриптор по имени окна возвращает функция FindWindow
На счет PID не очень понимаю о чем ты, на работе win98, а дома русс. winXP так что посмотреть что там высвечивается под PID не могу.
А вообще дескриптор окна - это просто шестнад. число типа - 000009D4. Но в любом случае, это знание нам не поможет, потому как дексриптор выдается системой автоматически, когда приложение запускается и и обычно все время разное.Так что его необходимо находить каждый раз, используя другие неизменные характеристика окна: заголовок, класс окна, по запустившему exe файлу или просто активное окно в данный момент.
Проще клавиатурную, потому как не надо задавать координаты клика мыши, а достаточно просто чтобы окно было активно (вопрос: в жизни при активном окне нажатие клавиши контекстного меню вызывает нужное меню? или тебе надо только то, которое возникает например когда щелкаешь мышкой по какому-нибудь элементу окна)
На счет PID не очень понимаю о чем ты, на работе win98, а дома русс. winXP так что посмотреть что там высвечивается под PID не могу.
А вообще дескриптор окна - это просто шестнад. число типа - 000009D4. Но в любом случае, это знание нам не поможет, потому как дексриптор выдается системой автоматически, когда приложение запускается и и обычно все время разное.Так что его необходимо находить каждый раз, используя другие неизменные характеристика окна: заголовок, класс окна, по запустившему exe файлу или просто активное окно в данный момент.
PID - это Process ID, идентификатор процесса. При запуске любой программы создается процесс, который, в свою очередь, может создавать окна. Дескриптор окна (handle) - это его идентификатор
И мне все равно, правую кнопку мышки мы туда пошлем или клавишу клавиатурную.
Посмотрите про дескрипторы окон на http://www.rsdn.ru/Forum/?mid=346642
Private Declare Sub keybd_event Lib "User32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const VK_APPS As Long = &H5D
Sub МакросПроба()
Dim hWnd As Long
Dim strCaption As String
strCaption = "Ля-ля" ' сюда надо ввести твой заголовок искомого окна
'Ищем hWnd программы по заголовку
hWnd = FindWindow(vbNullString, strCaption)
If hWnd>0 Then
Call keybd_event(VK_APPS, 0, 0, 0)
End If
End Sub
Пример:
Private Declare Sub keybd_event Lib "User32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const VK_APPS As Long = &H5D
Sub МакросПроба()
Dim hWnd As Long
Dim strCaption As String
strCaption = "Ля-ля" ' сюда надо ввести твой заголовок искомого окна
'Ищем hWnd программы по заголовку
hWnd = FindWindow(vbNullString, strCaption)
If hWnd>0 Then
Call keybd_event(VK_APPS, 0, 0, 0)
End If
End Sub
по-моему правильнее так:
Call keybd_event(VK_APPS, 0, 0, 0)
Call keybd_event(VK_APPS, 0, KEYEVENTF_KEYUP, 0)
плюс окно должно бытьв фокусе. Можно попробовать SetFocus, но по-моему эта фунцкия плохо работает с окнами-контролами
по-моему правильнее так:
Call keybd_event(VK_APPS, 0, 0, 0)
Call keybd_event(VK_APPS, 0, KEYEVENTF_KEYUP, 0)
Согласен, лучше добавить, чтобы освободить клавишу.
Тогда еще потребуется объявление:
Const KEYEVENTF_KEYUP = &H2
плюс окно должно бытьв фокусе. Можно попробовать SetFocus, но по-моему эта фунцкия плохо работает с окнами-контролами
Это обязательно. Если использовать SetFocus, то добавляем строку:
Declare Function SetFocus Lib "user32" Alias "SetFocus" (ByVal hwnd As Long) As Long
а в макрос, после If hWnd>0 Then:
SetFocus(hWnd)
по-моему правильнее так:
Call keybd_event(VK_APPS, 0, 0, 0)
Call keybd_event(VK_APPS, 0, KEYEVENTF_KEYUP, 0)
плюс окно должно бытьв фокусе. Можно попробовать SetFocus, но по-моему эта фунцкия плохо работает с окнами-контролами
Докладываю.
hwnd ловится. Правда, есть неудобство, связанное с тем, что название искомого окна имеет вид
"Приложение - Документ", где документы все время разные. Есть ли возможность искать окно в виде "Приложение*" ?
Если такой возможности нет - ничего страшного. Узнаю hwnd при неоткрытых документах.
А вот нажатие правой кнопки не передается.
Вообще, какой синтаксис у keybd_event?
Определили, и нигде не используем?
Вообще, какой синтаксис у keybd_event?
The keybd_event function synthesizes a keystroke. The system can use such a synthesized keystroke to generate a WM_KEYUP or WM_KEYDOWN message. The keyboard driver's interrupt handler calls the keybd_event function.
Windows NT/2000/XP: This function has been superseded. Use SendInput instead.
VOID keybd_event(
BYTE bVk, // virtual-key code
BYTE bScan, // hardware scan code
DWORD dwFlags, // function options
ULONG_PTR dwExtraInfo // additional keystroke data
);
Parameters
bVk
[in] Specifies a virtual-key code. The code must be a value in the range 1 to 254. For a complete list, see Virtual-Key Codes.
bScan
This parameter is not used.
dwFlags
[in] Specifies various aspects of function operation. This parameter can be one or more of the following values. Value Meaning
KEYEVENTF_EXTENDEDKEY If specified, the scan code was preceded by a prefix byte having the value 0xE0 (224).
KEYEVENTF_KEYUP If specified, the key is being released. If not specified, the key is being depressed.
dwExtraInfo
[in] Specifies an additional value associated with the key stroke.
Return Values
This function has no return value.
Remarks
An application can simulate a press of the PRINTSCRN key in order to obtain a screen snapshot and save it to the clipboard. To do this, call keybd_event with the bVk parameter set to VK_SNAPSHOT.
Windows NT/2000/XP: The keybd_event function can toggle the NUM LOCK, CAPS LOCK, and SCROLL LOCK keys.
Windows 95/98/Me: The keybd_event function can toggle only the CAPS LOCK and SCROLL LOCK keys. It cannot toggle the NUM LOCK key.
Example Code
The following sample program toggles the NUM LOCK light by using keybd_event() with a virtual key of VK_NUMLOCK. It takes a Boolean value that indicates whether the light should be turned off (FALSE) or on (TRUE). The same technique can be used for the CAPS LOCK key (VK_CAPITAL) and the SCROLL LOCK key (VK_SCROLL).
#include <windows.h>
void SetNumLock( BOOL bState )
{
BYTE keyState[256];
GetKeyboardState((LPBYTE)&keyState);
if( (bState && !(keyState[VK_NUMLOCK] & 1)) ||
(!bState && (keyState[VK_NUMLOCK] & 1)) )
{
// Simulate a key press
keybd_event( VK_NUMLOCK,
0x45,
KEYEVENTF_EXTENDEDKEY | 0,
0 );
// Simulate a key release
keybd_event( VK_NUMLOCK,
0x45,
KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
0);
}
}
void main()
{
SetNumLock( TRUE );
}
Requirements
Windows NT/2000/XP: Included in Windows NT 3.1 and later.
Windows 95/98/Me: Included in Windows 95 and later.
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.
Это нам не помешает? У меня Вин2000рус.
Это нам не помешает? У меня Вин2000рус.
keyb_event у меня работает и на 2000 и на ХР, хотя лучше использовать SendInput. Я только когда постил предыдущее сообщение заметил, что пользуюсь устаревшей функцией - очень удивился (то-то мне показалось странным анписание keyb_event)
По поводу: keybd_event(VK_APPS, 0, 0, 0) - должно работать, если токо в таком состоянии окно вообще воспринимает эту клавишу.
Работоспособность этой функции можешь проверить так:
Private Declare Sub keybd_event Lib "User32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const VK_APPS As Long = &H5D
Private Const KEYEVENTF_KEYUP = &H2
Sub МакросПроба()
Call keybd_event(VK_APPS, 0, 0, 0)
Call keybd_event(VK_APPS, 0, KEYEVENTF_KEYUP, 0)
End Sub
Блин, извиняюсь, это не надо.
У меня win98 и на моих програх keyb_ум работает, но можно конечно и через SendInput переделать, правда там чуть по сложнее, там ввиде структуры помойму вся инфа передается, надо тип объвлять
Все нормально передается. Но есть проблема.
Окно приложения выглядит так: собственно окно, в нем окно документа и еще одно вспомогательное окно. Контекстное меню мне нужно в этом вспомогательном окне.
AppActivate активирует собственно окно приложения, которое ввобще не отзывается на нужную клавишу (поэтому я и подумал сначала, что неправильно передается). Активировать всякими Tab-ами или чем-нибудь еще нужное мне окно не получается. Стало быть, придется генерировать нажатие мышкой...
М-да. Не все так просто...
Все нормально передается. Но есть проблема.
Окно приложения выглядит так: собственно окно, в нем окно документа и еще одно вспомогательное окно. Контекстное меню мне нужно в этом вспомогательном окне.
AppActivate активирует собственно окно приложения, которое ввобще не отзывается на нужную клавишу (поэтому я и подумал сначала, что неправильно передается). Активировать всякими Tab-ами или чем-нибудь еще нужное мне окно не получается. Стало быть, придется генерировать нажатие мышкой...
Это крайний вариант.
Все таки хорошо бы узнать класс окна этого приложения и его дочернего окна, где тебе надо контексное меню получить. Обычно я поступаю именно так и это наиболее простой и надежный вариант. А как найдем hWnd твоего вспомогательного окна, там и фокус можно сразу перевести и нажатие клавиши туде послать.
Надо тебе Spy++, будем искать твое окно.
Это все равно придется сделать, чтобы даже вычислить куда мышкой тыкать, так что hWnd вспомогательного окна нам все равно понадобится
и последний, масенький:
А у меня уже стоит Winspector. Подойдет?
Вот он мне что сказал про то вспомогательное окно:
ClassName: "Afx:400000:8:10013:0:b409df"
Text: "Data Window - Extended"
Ну и размеры там всякие...
(Я правда от WinInspectora отказался, у меня слишком все тормозит с ним и часто падает, старый Spy++ пошустрее гораздо и падает реже, правда иконку слева от окна в дереве не показывает :-( )
Прикольный у него класс, для гарантии посмотри, он не меняется после каждого запуска.
Теперь такой момент. Это окно дочернее, какие у него родители (их классы) или оно вообще отдельно?
Супер. то что доктор прописал...
(Я правда от WinInspectora отказался, у меня слишком все тормозит с ним и часто падает, старый Spy++ пошустрее гораздо и падает реже, правда иконку слева от окна в дереве не показывает :-( )
Прикольный у него класс, для гарантии посмотри, он не меняется после каждого запуска.
Теперь такой момент. Это окно дочернее, какие у него родители (их классы) или оно вообще отдельно?
Хехе.
Последние шесть символов - "b409df" меняются. Это какое-то шестнадцатиричное число (при втором запуске стало 4309b3). У него родитель - само приложение (класс "or.exe").
На самом деле меня не затруднит перед запуском макроса расположить это вспомогательное окно в нужном месте экрана или, скажем, посмотреть у него предварительно ClassName.
Ну я hwnd у вспомогательного окна поймал по названию. Так что покажите мне, пожалуйста, как воспользоваться SetFocus, и я думаю, что все должно получиться.
Ну что ж, тогда:
Declare Function SetFocus Lib "user32" Alias "SetFocus" (ByVal hwnd As Long) As Long
в макросе:
Dim i as Long
i=SetFocus(hWnd)
Если SetFocus не пойдет, то будем экспериментировать с SetActiveWindow и т.д.
А я пока посмотрю, на счет дочерних окон, если ты в 97, функция обратного вызова у тебя не пойдет, но есть кой-какие мысли
Public Declare Function FindWindowEx Lib "User32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Declare Function GetClassName Lib "User32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Sub МакросПроба()
Dim hWnd As Long, hWnd1 As Long
Dim strClassName As String
'Ищем hWnd главного окна по его классу
hWnd = FindWindow("or.exe", vbNullString)
Do
' получаем hWnd дочерн. окна
hWnd1 = FindWindowEx(hWnd, hWnd1, vbNullString, vbNullString)
' буфер для имени класса окна
strClassName = Space(256)
' получаем имя класса активного элемента
i = GetClassName(hWnd1, strClassName, 256)
' формат. к строке сравнения
strClassName = Left(strClassName, 21)
If strClassName = "Afx:400000:8:10013:0:" Then
' вспомогат. окно нашли, выходим
Exit Do
End If
Loop While hWnd1 > 0
'Ну и дальше используем тот кусок с SetFocus(hWnd1) и т.д. используя уже hWnd1 как дексриптор вспомогательного окна.
По имени, первая vbNullString - это имя класса, а вторая vbNullString - это имя заголовка окна, у меня нулевые, потому что я хотел по все дочерних пробежаться, а если добавишь свои данные, то будет искать еще в соотв. с теми именами (класса или заголовка или и того и другого).
Странно, что не находит, посмотри через breakpoint значение hWnd - оно равно реальному твоему приложению, сравни со значением в Wininspector, может оно вообще равно нулю (может у окна другой класс, не or.exe и поэтому не находит)...?
А вот, проще, добавь строку: Debug.Print strClassName перед строкой с If .... then и напиши, что она там понаписала в окне Debug
В Winspectore есть дерево всех окон.
В нем искомое вспомогательное лежит непосредственно в самом корневом "Окне" (видимо поэтому среди подокон or.exe и не находится). Но если вызвать свойства вспомогательного и попросить показать родителя - переходит к or.exe
Чтобы перелопатить все окна верхнего уровня,... надо подумать что тебе подойдет, где-то я тут видел нужную функцию