Страницы: 1
RSS
Изменяется Num Lock при %{down), При посыле команды Alt+Down меняется индикатор NumLock
 
При посыле команды Alt+Down меняется индикатор NumLock. (решено, пост №9)
По-моему это глюк Excel.

Прикрепил файл, но по сути он чист. Только в "Лист1" добавлен макрос ну и создан список для теста:

Код
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Dim lValidation As Long
    On Error Resume Next
     lValidation = ActiveCell.Validation.Type
    On Error GoTo 0
    If lValidation = 3 Then Application.SendKeys "%{DOWN}"
End Sub

Подробно.
Если выделить ячейку с проверкой данных, то посылается команда Alt+Down, что бы список сам раскрылся. Для удобства.
Однако вместе с этим, меняется индикатор NumLok, причем, если в какой-то момент нажать NumLock самому (пальцем на клавиатуре), то он как ему и положено изменится и больше меняться от выделения ячейки не будет.
Если нажать еще раз, то снова начнет меняться от выделения ячейки.

На другом форуме уже задавал этот вопрос, сказали смотри машину. Проверял на 4-х разных машинах (на одной 3 недели назад винду переустанавливали, так что чище некуда) с разными клавиатурами. Эффект один.

Как бы это пофиксить?
Изменено: Dmitri_0154 - 22.10.2014 17:41:23
 
При установке винды все равно устанавливают драйвера и специальные утилиты. Могу предположить, что на последнем компе поставили графическую утилиту от Nvidia или еще чего нибудь. Попробуйте у них поискать Hotkey-и и вообще отключить их
Учимся сами и помогаем другим...
 
Не указал сразу. Если на клавиатуре выполнить Alt + "стрелка вниз", то что при левом, что при правом Alt, NumLoсk не меняется. В т.ч. если выделена ячейка со списком. Список раскрывается и сворачивается, а NumLock не реагирует. Реагирует только на команду из скрипта.

У меня действительно nVidia, не знаю где там что может быть с хоткеями, но поищу, хоть и сомневаюсь что найду что-то.
И еще, отпишитесь кто-нибудь у кого именно этот макрос нормально выполняется, без смены NumLock (желательно проверить в приложенном файле). А то ощущение, что советуют не проверив. Просто для уверенности, мол "Я, такой-то проверял, у меня все как надо, ищи ошибку у себя". Редко бывает, что на разных машинах один и тот же глюк.
Изменено: Dmitri_0154 - 21.10.2014 14:37:41
 
Dmitri_0154, аналогично и у меня ведёт себя Ваш макрос. Вынь7х64, офисХ64. Нвидиа нет. Пунтосвитчер выгрузил. Причину не знаю.

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
та же фигня. у меня переключение NumLock работает при каждом выделении ячейки.
Это баг, описанный Microsoft
F1 творит чудеса
 
Ок, попробуем обойти. Где-то месяц назад я писал макрос с использованием этого трюка. На VBA до этого не писал так что уже подзабыл что да как. Смысл в том, что как и рекомендовано в Mictosoft по ссылке выше, я пытался запомнить значение NumLock и после восстановить его. Считывал значение 2-3 разными методами, но возвращалось всегда одно и то же значение. Так и не разобрался тогда. Может кто напишет пару строк работающего кода?

Схема проще некуда:
1. before = Get_NumLock()
2. Application.SendKeys "%{DOWN}"
3. Set_NumLock(before)

Но в 1-й строчке как ни крутил, всегда true возвращалось (или false, не помню уже) .
(на всякий случай: 1 и 3 строчки используют несуществующие методы Get/Set, это только пример как должно работать)

Предложения от Microsoft:
Чтобы обойти эту проблему, выполните одно из следующих:
Определите параметр клавиши NumLock до с помощью SendKeys. Затем, Отключите NumLock перед использованием SendKeys. После использования SendKeys, сброс NumLock его предыдущего значения. Это достигается с помощью GetKeyboardState, keybd_event и SetKeyboardState API функций. См. в разделе ссылки ниже для получения дополнительной информации.
ИЛИ
С помощью функций API вместо SendKeys
Изменено: Dmitri_0154 - 21.10.2014 16:14:36
 
Цитата
Dmitri_0154 пишет: 1. before = Get_NumLock()
вот так вроде бы возвращает статус в зависимости от лампочки.
Код
Private Declare PtrSafe Function GetKeyState Lib "user32.dll" (ByVal nVirtKey As Long) As Integer

Sub Get_NumLock()
   Dim before as Integer
   before = GetKeyState(vbKeyNumlock)
End Sub
А вот как установить статус кнопки обратно - это я не знаю  :)  пытался дописать в ваш код SendKeys({NUMLOCK}) - он моргает и сбрасывается. Наверняка есть какая-то API-функция, которая "нажимает" NumLock.
F1 творит чудеса
 
Не проверял, но гляньте тут.

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
И все-таки это возможно.

Коротко и с готовым примером (файл прилагается).


Перед
Код
Application.SendKeys "%{DOWN}"
надо выполнить
Код
If Num_Not_Stable() Then Application.SendKeys "{NUMLOCK}"
и NumLock меняться не будет.

В листе:
Код
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Dim lValidation As Long
    
    If Num_Not_Stable() Then Application.SendKeys "{NUMLOCK}" ' делает NumLock стабильным
    
    On Error Resume Next
      lValidation = ActiveCell.Validation.Type
    On Error GoTo 0
    If lValidation = 3 Then Application.SendKeys "%{DOWN}"
End Sub

В модуле книги:
Код
Declare PtrSafe Function GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long

' Определяет, изменчивое ли состояние у NumLock или нет
' Возвращает false - стабильный, true - изменчевый
Function Num_Not_Stable() As Boolean

    Dim keystat(0 To 255) As Byte
    Dim state As String
    
    GetKeyboardState keystat(0)
    state = keystat(vbKeyNumlock)
    
    If (state = 0) Then
     Num_Not_Stable = False
    Else
     Num_Not_Stable = True
    End If
End Function 

Теперь рассуждения что, и как работает.

Посмотрев по предложенным ссылкам и многим другим источникам, убедился- все это шляпа. И она не работает.
Я не то что не гуру, использую VBA около месяца. Потому, все что ниже (и выше, не счет "шляпы"  ;)  может не соответствовать истине.
Кроме того, что бы придти к этому способу я сознательно подменил стандартные понятия своими, заведомо не верными, но работающими в данном контексте.

И так.
Первое понятие: команда {NUMLOCK} не посылает сигнал "нажать NumLock", а закрепляет NumLock, если он "изменчивый".

"Изменчивый" - это когда он меняет свое состояние, когда не должен, например при команде %{DOWN} которая по идее должна только сымитировать Alt+Down.

Экспериментальным путем выяснил, что NumLock имеет "стабильное" состояние и "изменчивое". Не путать с "включен" и "выключен", в любом из них он может быть как "изменчивым" так и "стабильным".

Второе понятие: строчки
GetKeyboardState keystat(0)    
state = keystat(vbKeyNumlock)
определяют не текущее состояние NumLock, а его текущую стабильность. Если state = 0, то NumLock стабилен, иначе изменчив.

Теперь все просто.
Если NumLock стабилен, ничего не делаем, просто посылаем команду %{DOWN}.
Если NumLock изменчивый, посылаем команду стабилизации (закрепления) {NUMLOCK}, и после уже спокойно пишем %{DOWN}, зная, что NumLock не изменится.

Конечно понятия "стабильный" и "изменчивый" надуманы но тут они отлично подходят.

Несколько фактов из наблюдений.
В стабильном состоянии следущие команды не меняют NumLock
%{DOWN}
{NUMLOCK} {NUMLOCK} (т.е. два раз подряд послать нажатие NumLock)

В изменчивом состоянии те же команды его меняют регулярно (при каждой команде NumLock 1 раз меняется) оставляя его изменчивым.
При этом команда
{NUMLOCK} (т.е. один раз послать нажатие NumLock)
работает так (допустим сейчас стабильное состояние, и он включен (лампочка горит)):
1. {NUMLOCK} - NumLock изменился (погас), состояние стало изменчивым
2. {NUMLOCK} - NumLock не изменился (не горит),   состояние стало стабильным
3. {NUMLOCK} - NumLock изменился (загорелся), состояние стало изменчивым
4. {NUMLOCK} - NumLock не изменился (горит),  состояние стало стабильным
...... и далее по кругу


Еще немного мелочей.
Во многих гайдах пишут подключить библиотеку для работы GetKeyboardState keystat(0), а именно:
Declare PtrSafe Function GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long
PtrSafe - там отсутствует, что вызывает ошибку, т.к. сейчас почти у всех Windows 64 bit, а не 32. Для совместимости и надо добавить.

Так же в этом объявлении для совместимости с 64-битной версией, рекомендуют использовать тип LongLong вместо Long. Возможно это где-то оправдано. У меня работали оба варианта, но у одного из тех, кому я делал вылезла ошибка, что у него нет такого типа данных (LongLong). По этому пишу просо Long.
Что у меня, что у него Windows 7 64bit, Excel 2010.

P.S. мда, ну и накатал. Что значит заняться не чем было (5 часов разбирался). Ну да может пригодится кому.
 
Dmitri_0154, Ваша настойчивость в достижении результата достойна уважения, которое я Вам и выражаю снимая шляпу. :) (хотя в жизни шляп не носил и не собираюсь ;) )

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
Вот все бы так. Респект!
Учимся сами и помогаем другим...
 
Спасибо за пояснения.
Вот еще из MSDN, из пояснений к функции GetKeyState (так и не понял, зачем использовать GetKeyboardState, если мы хотим узнать только одну кнопку), какие она возвращает состояния (она возвращает Integer):
Цитата
0 if the key is neither down nor toggled,
-127 if the key is down but not toggled,
1 if the key is toggled but up, and
-128 if the key is both toggled and down.
где toggled - это якобы когда лампочка горит.
GetKeyboardState возвращает 129 вместо -127, 128 вместо -128
Соответственно, можно плясать от этих значений сразу, не пользуя UDF?

А вот то, что SendKeys "{NumLock}" не переключает сразу состояние, а бегает по этому кругу - вот это новость )) сейчас буду ее логгить
F1 творит чудеса
 
теперь понятно почему на этот код программа реагирует странно - то включает, то не включает. Всё дело в состоянии. А как тогда можно сделать нормально работающий код, который бы гарантировано включал NumLock при загрузке формы?
Код
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer

Public Function KeyboardNumLock()

Dim WsNumlock As Variant

If GetKeyState(vbKeyNumlock) = 0 Then
    Set WsNumlock = CreateObject("WScript.Shell")
    WsNumlock.SendKeys "{NUMLOCK}"
End If

End Function
Изменено: intersk - 23.03.2015 14:14:28
 
Может кому поможет. На основе статьи https://support.microsoft.com/ru-ru/kb/177674/en-us  написал следующее

ManageButtons (False) 'выключить кнопки NumLock, CapsLock, Scrol Lock
ManageButtons (True) 'включить кнопки NumLock, CapsLock, Scrol Lock

Код
Option Explicit
    
Private Declare Function GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

Const VK_NUMLOCK = &H90
Const VK_SCROLL = &H91
Const VK_CAPITAL = &H14
Const KEYEVENTF_EXTENDEDKEY = &H1
Const KEYEVENTF_KEYUP = &H2
    
Sub Управление_кнопками()
    ManageButtons (False) 'выключить кнопки
    ManageButtons (True) 'включить кнопки
End Sub

Private Sub ManageButtons(ByVal State As Boolean)
'https://support.microsoft.com/ru-ru/kb/177674/en-us

    Dim NumLockState As Boolean
    Dim ScrollLockState As Boolean
    Dim CapsLockState As Boolean
    
    Dim keys(0 To 255) As Byte
    GetKeyboardState keys(0)

    ' NumLock
    NumLockState = keys(VK_NUMLOCK)
    If NumLockState <> State Then
        keybd_event VK_NUMLOCK, &H45, KEYEVENTF_EXTENDEDKEY Or 0, 0 'Simulate Key Press
        keybd_event VK_NUMLOCK, &H45, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0 'Simulate Key Release
    End If
    
    ' CapsLock
    CapsLockState = keys(VK_CAPITAL)
    If CapsLockState <> State Then
        keybd_event VK_CAPITAL, &H45, KEYEVENTF_EXTENDEDKEY Or 0, 0 'Simulate Key Press
        keybd_event VK_CAPITAL, &H45, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0 'Simulate Key Release
    End If
    
    ' ScrollLock
    ScrollLockState = keys(VK_SCROLL)
    If ScrollLockState <> State Then
        keybd_event VK_SCROLL, &H45, KEYEVENTF_EXTENDEDKEY Or 0, 0 'Simulate Key Press
        keybd_event VK_SCROLL, &H45, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0 'Simulate Key Release
    End If
End Sub
 
intersk

В Access столкнулся с проблемой переключения NumLock при использовании SendKeys. Помог WS. При нажатии одной кнопки надо, чтобы "нажалась" другая. Если использовать SendKeys - NumLock переключается, а так:
Код
Private Sub NB2_Click()
   Me.NB1.SetFocus: SendSomeKey "~", -1
End Sub

Sub SendSomeKey(Code As String, Optional Wait As Boolean)
   Dim ws As Variant

   Set ws = CreateObject("WScript.Shell")
   ws.SendKeys Code, Wait
   Delay 0.5 ' подождать 1/2 секунды любым доступны в VBA способом
End Sub
НЕТ, не переключается.
Спасибо.  
 
Спасибо за сообщение. Да, я тоже замечал, что при использовании метода SendKeys объекта Wscript.Shell проблемы с переключателями NumLock и CapsLock не возникают (в отличие от оператора VBA SendKeys и метода Application.SendKeys).
Владимир
 
Вот и я столкнулся с этой проблемой и воспользовался исследованиями предыдущих ораторов. Мои пять копеек:
Не зря предостерегают от SendKeys: мало того, что во время работы клавиатуру не тронь, так еще и мусорит, и "соседние кнопки" умудряется задеть.

Результат проверки GetKeyboardState и GetKeyState - это, насколько я понял: считается ли клавиша НАЖАТОЙ или ОТПУЩЕННОЙ!!! Именно эти состояния автор темы именовал "стабильным" и "изменчивым" (только в каком сочетании - не разобрался я)
А SENDKEYS, как я понял, передает за один раз не целое переключение: кнопка нажата и отпущена, а только его половину. Такие переключатели, как NUMLOCK совершают цикл за два целых переключения, стало быть, за 4 SENDKEYS. Вот и объяснение 4-х состояний для NUMLOCK.

Мне, кстати, не удалось добиться от  GetKeyboardState и GetKeyState ничего, кроме 0 или 1, свидетельствующих (как уважаемые форумчане точно подметили) нажата клавиша или отпущена.

В одном из этих состояний (честно, не стал досконально разбираться в каком: при нажатой, но не отпущенной или при ненажатой клавише) SENDKEYS "^©" своего соседа NUMLOCK "задевает и переключает", а в другом - не трогает! Но переключая значения, не изменяет нажатость!!!
ЕЩЁ!!!! Я проверил эффект SENDKEYS "^©" для состояния клавиши Ctrl - у нее тоже меняется состояние нажатости и однократное нажатие "мусорит" и здесь! Как-то потом техника справляется, а, может, и не справляется. В общем, мусор от SendKeys остается и там!
Время от времени обращал внимание: слетал режим INSERT, но реже и с ним разбираться не стал.

Предложенный костыль через "WScript.Shell" соседей не задевает, но мусор неотжатой кнопки Ctrl оставляет все равно.

Как обойти? Можно, конечно, проверять для NUMLOCKа GetKeyState и если он 1, то после SendKeys "^©" дважды SENDKEYS "{NUMLOCK}

Самый чистый, на мой взгляд - двукратный посыл SendKeys "^©"! Но если в коде их сделать подряд, то система не успевает реагировать и эффект получается как от одного, поэтому нужно поставить между ними ожидание (на моем компе хватило и 0,01 секунды). Это, кстати, касается и двукратного SENDKEYS "{NUMLOCK}.

Итак. на мой взгляд решение может быть таким:
Код
SendKeys "^(c)", True
    tim = Date * 24 * 3600 + Timer + 0.01
    Do
        DoEvents
    Loop While tim > (Date * 24 * 3600 + Timer)
SendKeys "^(c)", True

P.S.: еще ложечка деготка: эффект SendKeys "^©" зависит от раскладки клавиатуры!!!!!
При русской раскладке он мне вводил "c" вместо копирования выделенного!
Поэтому перед всеми SendKeys лучше запомнить текущую раскладку, сменить раскладку на латиницу, а потом вернуть ту, что была.

Прикладываю для пытливых умов файл с результатами экспериментов. Поскольку не обнаружил: как проверять "лампочку NUMLOCKа", то заносил ручками да/нет


Изменено: PerfectVam - 03.06.2022 22:47:14 (не загрузился файл)
Следствие из третьего закона Чизхолма:
"Даже если ясность изложения исключает неверное толкование, все равно найдется кто-то, кто поймет Вас неправильно."
Страницы: 1
Наверх