Как создать событие для листа Excel?
Ставим в макросе hook (ловушку) типа - WH_JOURNALRECORD. Для этого надо создать модуль со следующим кодом:
Public Type POINTAPI
x As Long
y As Long
End Type
Public Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Public Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes As Long)
Private Declare Function GetForegroundWindow Lib "user32" () As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Private Const WM_MOUSEMOVE = &H200
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202
Private Const WM_LBUTTONDBLCLK = &H203
Private Const WM_RBUTTONDOWN = &H204
Private Const WM_RBUTTONUP = &H205
Private Const WM_RBUTTONDBLCLK = &H206
Private Const WM_MBUTTONDOWN = &H207
Private Const WM_MBUTTONUP = &H208
Private Const WM_MBUTTONDBLCLK = &H209
Private Const WM_MOUSEWHEEL = &H20A
Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101
Public Const WH_JOURNALRECORD = 0
Type CBTACTIVATESTRUCT
fMouse As Long
hWndActive As Long
End Type
Dim CBT As CBTACTIVATESTRUCT
Public hHook As Long
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
' Значение этих констант может зависеть от версии Office!
' Необходимо через Spy++ или WinIspector определить классы окон в Вашей версии Office.
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Const cClassNameXLMAIN As String = "XLMAIN" ' имя класса главного окна Excel'а
Const cClassNameWrbook As String = "EXCEL7" ' имя класса окна workbook Excel'а
' свои служеб. переменные
Public sNameBook As String ' имя книги для которой необходимо отслеживать событие MoveMouse,
' меняем из любого места программы.
Public Function HookProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim WinWnd As Long
Dim lpClassName As String
Dim sNameWin As String
Dim i As Long
' получаем дескриптор активного окна
WinWnd = GetForegroundWindow()
' буфер для имени класса окна
lpClassName = Space(256)
' получаем имя класса активного окна
i = GetClassName(WinWnd, lpClassName, 256)
' от пробелов освобождаемся
lpClassName = Left(lpClassName, i)
If lpClassName = cClassNameXLMAIN Then ' если активное окно не Excel, то не мучаем систему
' и больше ничего не делаем.
CopyMemory CBT, ByVal lParam, Len(CBT)
Select Case CBT.fMouse
' событие - движение мыши
Case WM_MOUSEMOVE
Dim CurPos As POINTAPI
' получаем новые коорд.
GetCursorPos CurPos
' получение hWnd окна под курсором
WinWnd = WindowFromPoint(CurPos.x, CurPos.y)
' опр. имя класса по найденному hWnd
lpClassName = Space(256)
i = GetClassName(WinWnd, lpClassName, 256)
lpClassName = Left(lpClassName, i)
If lpClassName = cClassNameWrbook Then ' курсор над workbook (какой-то, пока еще не известно какой)
' создание стр. перем. необходимой длинны
sNameWin = String(GetWindowTextLength(WinWnd) + 1, Chr$(0))
' получение заголовка окна (в нем название акт. Excel'ского файла)
GetWindowText WinWnd, sNameWin, Len(sNameWin)
'Debug.Print sNameWin 'отладка
If sNameWin = sNameBook Then
' курсор движется над нужной (sNameBook) workbook
' вызываем свою функцию для обработки события MouseMove над книгой
Call MouseMoveOnWorkbook(CurPos.x, CurPos.y)
End If
End If
Case WM_KEYDOWN
Debug.Print "KEYDOWN"
Case WM_KEYUP
Debug.Print "KeyUp"
Case WM_MOUSEWHEEL
Debug.Print "MOUSEWHEEL"
Case WM_LBUTTONDOWN
Unload UserForm2
Debug.Print "LBUTTONDOWN"
Case WM_LBUTTONUP
Debug.Print "LeftUp"
Case WM_RBUTTONDOWN
Debug.Print "RBUTTONDOWN"
Case WM_RBUTTONUP
Debug.Print "RightUp"
Case WM_MBUTTONDOWN
Debug.Print "MBUTTONDOWN"
Case WM_MBUTTONUP
Debug.Print "MiddleUp"
End Select
End If
HookProc = CallNextHookEx(hHook, nCode, wParam, lParam)
End Function
В этом модуле описывается функция HookProc, которая вызывается, когда твоя прога устанавливает hook (ловушку). В ней же идет анализ перехваченных событий в системе.
В Win98 установленная таким образом ловушка будет глобальной, на всю систему, а вот под другие оси возможно ограничения только процессом установшим эту ловушку (нам и такой и вариант для Excel'а в принципе подходит). Дело в том, что по хорошем, для глобальных hook их функцию HookProc надо бы засовывать не в модуль, а в Dll'ку (но, вообщем, для такого типа ловушек - WH_JOURNALRECORD и для наших задач и так пока сойдет, а написание своих dll это уже совсем другая история).
Когда модуль будет создан и включен в проект, необходимо будет в своем коде установить ловушку, а потом еще не забыть ее удалить. Это делается так:
hHook = SetWindowsHookEx(WH_JOURNALRECORD, AddressOf HookProc, 0, 0)
а удалить:
Call UnhookWindowsHookEx(hHook)
(переменная hHook уже объявлена в модуле, так что объявлять ее еще раз в коде макроса не надо)
Несколько слов по коду модуля:
1. Как я там написал, константы cClassNameXLMAIN и cClassNameWrbook могут зависеть от версии Office и их хорошо бы перепроверить в твоем случае через программы: Spy++ или WinIspector.
2. Публичная переменая sNameBook - для указания названия книги, для которой необходимо отслеживать движение мыши (если все равно над какими книгами, то код легко упростить, убрав лишнюю проверку)
3. В итоге событие возникает при движении мыши над книгой, без проверки названия листа - до него не смог добраться. Но это просто сделать и обычными средствами Ecxel'а через ThisWorksheet в самом коде макроса, получив известие о движении мыщи в пределах выбранной книги (так же можно уточнить и название книги ThisWorkbook, соответ. убрав из кода модуля тогда уже не нужную проверку на название активной книги).
Вообщем, код твой, смотри, меняй, кромсай :)
месяцев.:)
Хочу только добавить пару моментов, с которыми столкнулся.
1. Перед всеми функциями типа(Chr,Left,Space$) нужно проставить имена модулей,
к которым они принадлежат(напр. strings.Space$), пока я это не сделал,excel при запуске
макроса постоянно зависал.
2. Согласно документации по HookProc (Borland win32) - если был поставлен хук типа
WH_JOURNALRECORD, то параметр lParam функции HookProc указывает на структуру EVENTMSG, в
которой 5 параметров. В примере копируется этот блок памяти в структуру CBTACTIVATESTRUCT,
у которой всего 2 параметра. И хотя все работает нормально, в виду того что работа с
памятью для меня темный лес я не совсем понял как так может быть. Кстати, я попытался
записывать lParam в структуру EVENTMSG, тоже сработало.
Еще раз спасибо, здорово выручил.
2. Согласно документации по HookProc (Borland win32) - если был поставлен хук типа
WH_JOURNALRECORD, то параметр lParam функции HookProc указывает на структуру EVENTMSG, в
которой 5 параметров. В примере копируется этот блок памяти в структуру CBTACTIVATESTRUCT,
у которой всего 2 параметра. И хотя все работает нормально, в виду того что работа с
памятью для меня темный лес я не совсем понял как так может быть. Кстати, я попытался
записывать lParam в структуру EVENTMSG, тоже сработало.
Так там в структуре поля (fMouse, hWndActive) и копирование происходит по полям (в получаемом lParam тоже поля), соотв. если в структуре указанных полей нет, то они пропускаются, а заполняются только какие есть.
Насчет хука типа - WH_JOURNALRECORD, есть у меня милок подозрение. Нет у меня у меня доверия к хуку, который запущенный из модуля должен перехватывать (по MSDN) сообщения только своей программы, а в Win98 перехватывает все, как глобальный. Не проверял, но говорят, что на других платформах он так не пашет, токо в Win98. И еще программа с таким хуком у меня периодически вешает систему - грешу на него.
Так что если есть время и желание, то сделай все по уму и засунь его в отдельную dll или используй уже готовые (их много в инете разных)