Глобальная клавиатура хук для (Excel) надстройки автоматизации (не VSTO) - PullRequest
0 голосов
/ 24 октября 2011

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

Мне было поручено обновить старое дополнение Excel UDF (это было ранееиспользование кода API из книги Стива Далтона с JNI и практически всех других технологий, которые только можно вообразить).Функции были очень несимпатичны в расчетной модели Excel - эти функции брали несколько диапазонов и записывали данные в них (в другом потоке), в то же время позволяя пользователям редактировать эти данные (которые затем сохранялись и загружались на удаленный сервер).Все это сделало загрузку крайне медленной, но дало пользователям необходимую гибкость для изменения данных по мере необходимости.

Я перенес их на C # (он принимает данные с удаленного сервера Java через WSDL), ноЯ обнаружил, что большинство функций вызывалось более 50 раз, и оно выполнялось намного медленнее, чем исходное дополнение (это расширение для автоматизации, использующее Extensibility.IDTExtensibility2 - так что здесь нет доступных трюков VSTO).

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

Понимание того, что Excel не предоставил каких-либо событий обратного вызова проверки перед редактированием, фиксируется (я поиграл с Worksheet.OnEntry и добавил VBComponent, но он не запускается до ошибкио перезаписи массива или проверки данных списка).

Я предполагал, что было бы достаточно просто реализовать глобальный хук клавиатуры, поэтому начал писать некоторые оконные формы для перехвата изменений (просто TextBox для отдельных записей формы и ComboBox для ячеек с проверкой данных списка), а также получил его, копируя данные избуфер обмена в выбранном диапазоне.

В настоящее время все это основано на специальных записях контекстного меню (или двойном щелчке), которые не будут приняты пользователями - я должен, как минимум, иметь возможность перехватывать F2, Ctl + V ипрямая печать на активной ячейке.Но я понятия не имею, как зарегистрировать глобальный хук клавиатуры в надстройке автоматизации.

Так что мой вопрос таков;Можно ли перехватить каждую попытку редактирования и предоставить мою собственную обработку?Или, если это не удалось, как я могу зарегистрировать глобальный хук клавиатуры для перехвата F2, Ctl + V и прямой печати на активной ячейке?

Я пробовал хук, найденный здесь Использование глобальной клавиатурыловушка (WH_KEYBOARD_LL) в WPF / C # , но не может заставить App.xaml + App.xaml.cs работать в этом контексте (это мое первое знакомство с C # и программированием Windows в целом), поэтому, возможно, кому-то просто нужнопросветите меня относительно конфигурации App.xaml + App.xaml.cs ().

Обратите внимание;это не надстройка VSTO, она реализована с использованием Extensibility.IDTExtensibility2.

ОБНОВЛЕНИЕ ОБНОВЛЕНИЯ: @TimWilliams и @CharlesWilliams поинтересовались, почему мои предыдущая версия , чтовыполнял чтение-запись аргументов своих функций, имел так много повторных вызовов.

Все функции имеют обязательный параметр ключа id, большинство также принимают диапазон дат или дат, следующее происходит (довольнобольшая рабочая книга ~ 30 листов с графиками):

  1. Когда книга загружается впервые, функция вызывает весь пожар с устаревшими (ранее сохраненными) значениями, но они игнорируются как первая строка C # вкаждая функция является (быстрым) тестом на основе модели поддержки, чтобы увидеть, была ли загружена модель, аргументы функции не проверяются / не разбираются.

  2. Пользователь выбирает загрузку модели через веб-сервисыили ранее сериализованный файл.

  3. Визуальные обновленияотключено, и лист настройки заполняется некоторыми данными по порядку; некоторые ключевые даты (начало и конец дат - различные диапазоны дат на разных листах рассчитываются с помощью EMONTH +/- 12), некоторые другие статические данные (имя автора / редактора и т. д.) и, наконец, обязательный идентификатор ключа (который идентифицирует данные модели) )

  4. Теперь у каждого метода функции в надстройке автоматизации есть ключ id, поэтому, если данные найдены, они возвращаются, в противном случае используются значения по умолчанию из параметров функции. (Примечание. Модель поддерживает копию полей объектов, запрошенных функциями, поэтому она знает, что было выведено. Для дальнейших вызовов, если данные присутствуют в слое кэша, они обновляются (клонирование вручную исходных данных модели или предыдущих значение кэша) с параметрами входящей функции - разность уровня кэша и данных модели позднее загружается в веб-сервис) - не очень хорошо объясненный «кэш» на самом деле является оберткой вокруг той же структуры данных, что и данные модели

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

Плохая производительность, кажется, связана с очень длинными цепочками зависимых функций (сбивает с толку Excel?) И тем фактом, что функции, похоже, вызываются до их зависимостей, например,

Учитывая, что DATE_RANGE представляет собой цепочку A1-An, An = EMONTH (An-1,12), где A1 - это константа LAST_DATE со страницы настройки, которая уже была заполнена

Затем fn (MODEL_ID, DATE_RANGE) вызывается после заполнения именованной ячейки MODEL_ID, но DATE_RANGE имеет неверные значения, и fn вызывается многократно по завершении каждого EMONTH, и методы функции пытаются преобразовать диапазоны в даты (если недопустимые даты потом возвращаюсь рано). Тем временем рабочий поток начинает генерировать исключения, связанные с занятостью приложения (и, таким образом, помещает в очередь записи и спящие диапазоны в течение произвольного периода 250 мс). В конце концов спор утихает, но у вас будет шанс приготовить и начать пить кофе первым (возможно, даже перемолоть бобы).

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

Я думаю, что доступны следующие варианты:

  • В версии редактирования перехвата попробуйте vK перехватить OnKey с вызовом для каждой возможной функции ASCII для обратного вызова в параметризованную команду C # (код VB можно по крайней мере сгенерировать в цикле)
  • Попробуйте изменить версию перехвата как дополнение VSTO (это должно дать мне привязки клавиш)
  • Использовать ExcelDNA - выглядит заманчиво для (предыдущей) версии параметра диапазона чтения-записи (которая может доказать достаточную производительность (что, вероятно, указывало бы на логическую ошибку в моем коде обработки Excel).

(еще раз извиняюсь за длину и отсутствие ясности)

Ответы [ 2 ]

1 голос
/ 25 октября 2011

Вы должны быть в состоянии решить вашу цепочку вычислений, так как она звучит как отдельный набор последовательных шагов.Если вы используете C ++ / XLL, вы должны сделать аргументы функции типа P, которые гарантировали бы, что они были вычислены Excel перед передачей в UDF.Я думаю, что Ex cel DNA / addin Express должен иметь такой же эффект, если параметры определены как что-то отличное от Object.Excel вычисляет ячейки в последовательности LIFO, которая установлена ​​предыдущей конечной последовательностью вычислений, и любые ячейки, которые были введены / изменены: таким образом, последняя измененная формула вычисляется первой.Таким образом, вы должны вводить формулы в цепочке DATE_RANGE в обратной последовательности (последняя в цепочке первой)Предположительно, вы уже переходите в режим ручного расчета в начале этого процесса.Таким образом, это может быть так же просто, как выписать таблицу настроек и даты, затем принудительно выполнить расчет (Application.calculate), затем обновить MODEL_ID, а затем выполнить другой расчет.И, конечно, при использовании Excel ДНК накладные расходы на вызов функции в любом случае будут намного ниже.см http://fastexcel.wordpress.com/2011/07/07/excel-udf-technology-choices-snakes-ladders-with-vba-vb6-net-c-com-xll-interop/

1 голос
/ 24 октября 2011

Я не могу помочь вам с глобальным подключением клавиатуры, но вам действительно нужно взглянуть на Excel DNA или Addin Express, чтобы значительно улучшить производительность ваших UDF на C # (они взаимодействуют .NET с XLL C API, который НАМНОГО быстрее, чемавтоматизация c #).И Excel DNA, и Addin Express также имеют темы на своих форумах поддержки, обсуждающие, как переписать данные обратно из UDF в другие диапазоны.IIRC Excel DNA обсуждает подход с использованием отдельных потоков, а Addin Express обсуждает использование UDF-команд типа эквивалентных команд для запуска скрытой функции XLMИ лично я думаю, что будет очень трудно заставить ваш глобальный подход к подключению клавиатуры работать ненавязчиво и эффективно также при любых обстоятельствах (открыты несколько рабочих книг, VBA, DDE и т. Д.).

...