1. Требуются разработчики и тестеры для проекта codebyOS. Требования для участия в проекте: Знание принципов работы ОС на базе Linux; Знание Bash; Крайне желательное знание CPP, Python, Lua; Навыки системного администрирования. Подробнее ...

    Скрыть объявление

Послать "горячие клавиши в другое приложение"

Тема в разделе "MS Visual C++", создана пользователем Serge, 2 фев 2004.

Статус темы:
Закрыта.
  1. Serge

    Serge Гость

    Репутация:
    0
    Послать сочетание клавматуры в другое приложение

    Немного истории. Недавно мне понадобилось переписать программы для переводов в WORD и Stylus3.1. на Word и Promt6. Это только в PROMTе считают, что этот кошмар, "профессиональная среда для переводов". В отличие от Stylus, где весь интерфейс расписан в одной библиотеке, в Promt6 все намного сложнее. Попросил на сайде Promt справку по библиотекам типов, но меня отфутболили, потребовав регистрацию. Пожав плечами - какая может быть регистрация на библиотеки типов, если я рекламирую их продукт?

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

    В предшествии у меня был, небольшой опыт в этом вопросе, в основном, что-то разблокировать в инстоляции, ну, в общем, по мелочам.

    Не открою никому Америку, что работа Windows базируется на сообщения между окнами и системой. Существует основной класс окна приложения и может быть подклассы окон приложения, и взаимодействуют они между собой с помощью сообщений, как стандартных, так и пользовательских.

    А вот теперь, в интернете, я только вскользь, находил, а какими сообщениями они пользуются между собой? И почему - то все скопом мило умалчивают об этом.

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

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

    Суть, заключается в том, послав сообщение активному окну, т.е. нажав сочетание клавиш, в активном окне приложения. Окно определяет, что есть нажатие "горячих клавиш" и посылает в верхнее окно (необязательно в Z порядке) WM_COMMAND или WM_SYSCOMMAND, в основном, со стандартными сообщениями Windows, в качестве параметров. А вот после этого оконная процедура, как правило, она одна на приложение, реагирует на это сообщение.

    Просто послать "сочетание" в другое приложение с помощью SendMessage или Post Message, вам не удаться никогда, если, только извернуться так, что удаться сымитировать работу драйвера клавиатуры. Но думаю, что не стоит ломать стену, если рядом не дыра, а "Бранденбурские ворота, " в которые вы можете въехать и измываться над любым приложением насколько у вас хватит фантазии.

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

    Идея в следующем:

    1. Переделать специализированный словарь от Stylus - в пользовательский, а затем подключить его к PROMTу

    2. Созданный вновь словарь надо назвать "InfoStylus", в комментариях старался поподробней описать, на тот случай если кому - то понадобиться сделать тоже самое.

    3. В командной строке запишите количество статей, которое необходимо переработать в пользовательский словарь. На практике 2000 статей обрабатывает нормально.

    4. Написана на Builder6

    Код:
    #include <vcl.h>
    
    #pragma hdrstop
    
    //---------------------------------------------------------------------------
    
    
    
    #pragma argsused
    
    //дескриптор исходного словаря
    
    HWND hWnd0 = NULL;
    
    // дескриптор словаря слива статей
    
    HWND hWnd1 = NULL;
    
    // верхнее окно hWnd0
    
    HWND hWnd01 = NULL;
    
    // верхнее окно hWnd1
    
    HWND hWnd11 = NULL;
    
    //главное окно
    
    HWND PAPA = NULL;
    
    char dcv[80];
    
    int i;
    
    bool t6;
    
    bool CALLBACK EnumWindowsProc(HWND, LPARAM);
    
    bool CALLBACK EnumWinChild (HWND, LPARAM);
    
    
    
    int main(int argc, char* argv[])
    
    {   //определение PAPA
    
      EnumWindows((WNDENUMPROC)EnumWindowsProc, 0);
    
    
    
    if (t6 == 1)
    
    {
    
    //да, закройте все окна в Stylus, программа особенно не тестировалась, а нижняя функция нужна на случай, если забыли это сделать
    
      EnumChildWindows(GetTopWindow(GetTopWindow(PAPA)), (WNDENUMPROC)EnumWinChild, 0);
    
    //Определение дескрипторов словарей
    
    EnumChildWindows(GetNextWindow(GetTopWindow(GetTopWindow(PAPA)), GW_HWNDNEXT), (WNDENUMPROC)EnumWinChild, 0);
    
    //определение дескриптора старшего окна исходного словаря
    
    hWnd01 = GetParent(GetParent(hWnd0));
    
    //определение дескриптора старшего окна слива словаря
    
      hWnd11 = GetParent(GetParent(hWnd1));
    
    //записать в командную строку количество статей, которые хотите скопировать  
    
    //практически перегонял словарь, то выявились некоторые заморочки:
    
    //пользовательский словарь должен быть < 300 kb,
    
    // сразу ставить не более 2000 статей, иначе у Stylus съезжает крыша
    
     for (i == 0; i < atoi(argv[1]); i++)
    
      {
    
    // переход в окно словаря слива
    
    PostMessage (hWnd11, WM_SYSCOMMAND, SC_NEXTWINDOW, 0);
    
    /*задержка, все-таки PostMessage функция неуправляемая, а динамический приоритет у этой программы на данный момент самый высокий из выполняющихся программ, приходится делать задержку или менять приоритет, что труднее. Цифры взяты с потолка, они не факт*/
    
     Sleep(10);
    
    //в определениях можно найти определение e122, что-то WM_COPY, легче так вставить, не проверял
    
    PostMessage(PAPA, WM_COMMAND, 0x1e122, 0);
    
     Sleep(10);
    
    // постановка сообщения в окно "Стрелка вниз"
    
    //кстати послать нажатие клавиши можно, но не получиться просто так послать сочетание
    
    //чем вызвано не знаю, скорее всего, какими-то временными интервалами в оконной процедуре
    
    // если кто может подсказать напишите "finkeviche@yandex.ru"
    
    PostMessage (hWnd0, WM_KEYDOWN, 0x28, 0);
    
    PostMessage (hWnd0, WM_KEYUP, 0x28, 0);
    
    Sleep(10);
    
    // переход в словарь слива (помните, что все остальные окна должны быть закрыты)
    
    //словарь должен быть назван "InfoStylus.udc"(потом можно его переименовать и вообще, что хотите, то и сделать и конвертировать его в PROMT6)
    
    //и имя его должно тоже быть "InfoStylus", иначе не определится дескриптор
    
    PostMessage (hWnd01, WM_SYSCOMMAND, SC_NEXTWINDOW, 0);
    
    Sleep(10);
    
    // вставить из буфера обмена статью в словарь слива
    
    //в определениях можно найти определение "e125", что-то вроде ID_EDIT_PASTE,
    
    //а 1 в старшем слове определяет, что используется клавиатура
    
    //ну и все такое, кому интересно
    
    PostMessage(PAPA, WM_COMMAND, 0x1e125, 0);
    
     Sleep(100);
    
      }
    
    }
    
      return 1;
    
    
    
    }
    
    //---------------------------------------------------------------------------
    
    
    
    bool CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
    
    {
    
     GetClassName(hWnd, dcv, 60);
    
      int ptr;
    
      ptr = strcmp(dcv, "TStylusWindow");
    
       if (ptr == 0)
    
      {
    
       PAPA = hWnd;
    
       t6 = true;
    
       return false;
    
      }
    
      t6 = false;
    
      return true;
    
    };
    
    
    
    bool CALLBACK EnumWinChild (HWND hWnd, LPARAM lp)
    
    {
    
       int ptr;
    
       GetClassName(hWnd, dcv, 60);
    
       ptr = strcmp(dcv, "SysListView32");
    
       GetWindowText(hWnd, dcv, 70);
    
       if (ptr == 0 && strcmp(dcv, "List1") == 0)
    
       {
    
       GetWindowText(GetParent(GetParent(hWnd)), dcv, 80);
    
       if (strncmpi(dcv, "InfoStylus:", 11) == 0)
    
       {
    
          hWnd1 = hWnd;
    
       }
    
       else
    
       {
    
          hWnd0 = hWnd;
    
       }
    
    
    
       }
    
       return true;
    
    }
    
    
    
    //=============================================
    
    А ниже приведен код на VBA, чтобы заставить PROMT6 переводить в Wordе. Необходимость этого в том, что если задействовать стандартные средства переводчика и предоставленный интерфейс в редакторе(Word), не получатся слитное выполнение общей программы, т.к. в памяти от переводчика болтается сервисная программа и ей фиолетово, что делается в редакторе, и связки не получатся. Пробовал отлавливать потоки на количество, но в сервисной программе, кажется, идет подсчет ссылок и отказывается переводить вообще. В сервисном модуле постоянно присутствует 4 потока, и она отшивает все, что не зарегистрировано PROMTом.
    
    Программа переводит выделенный текст и возвращает перевод из переводчика и оставляет его выделенным
    
    'StylusMacros
    
    'объявления на уровне модуля
    
    Dim Doc As Object
    
    Dim Promt As Object
    
    Dim rfgt55 As Integer
    
    Option Explicit
    
    'так удобно передавать в другие модули
    
    'параметры мои и особенно не обращайте внимание на них
    
    Sub StylusFlaish_t(SrtingProgramm$, yj As Long, dc As String)
    
    
    
    Select Case SrtingProgramm$
    
     Case "TransSelection"
    
       TransSelection (yj)
    
     Case "TransDocument"
    
       TransDocument
    
     Case "PromViz"
    
       PromViz
    
     Case "PromNoViz"
    
     PromNoViz
    
    End Select
    
    Private Sub TransSelection(yj As Long)
    
    Dim myTask As Task
    
    Dim Ran As Object
    
    If Doc Is Nothing Then
    
    'параметра можно откапать из реестра
    
     Set Promt = CreateObject("Promt.Application")
    
    ' делать PROMT видимым, если 0, то скрытым
    
     Promt.Visible = 1
    
     Set Doc = Promt.PromtDocuments.Add(1, "PROMT.Document")
    
    Else
    
    'переход в окно английского текста на всякий случай
    
       For Each myTask In Tasks
    
         If InStr(myTask.Name, "PROMT") > 0 Then
    'H111 код по определению WM_COMMAND, а 8007 переход в окно исходного текста
    
    'ну, 1 в старшем слове, я уже объяснял
    
           myTask.SendWindowMessage &H111, &H18007, 0
    
           Exit For
    
         End If
    
       Next myTask
    
    'см. справку по библиотекам типов (описание пользование макросами), которая поступает с PROMT
    
    'разработчики все - таки разжалобились
    
       Doc.Text = ""
    End If
    
    
    Set Ran = Selection.Range
    
    Ran.Copy
    
    Doc.Paste
    
    'вообще-то из-за этой функции и пришлось использовать COM интерфейс переводчика
    
    Doc.Translate
    'переход в окно перевода
    
     For Each myTask In Tasks
    
       If InStr(myTask.Name, "PROMT") > 0 Then
    
         myTask.SendWindowMessage &H111, &H18008, 0
    
        End If
    
     Next myTask
    
    'все выделить
    
      For Each myTask In Tasks
    
       If InStr(myTask.Name, "PROMT") > 0 Then
    
         myTask.SendWindowMessage &H111, &H1E12A, 0
    
        End If
    
     Next myTask
    
    'скопировать в буфер
    
      For Each myTask In Tasks
    
       If InStr(myTask.Name, "PROMT") > 0 Then
    
         myTask.SendWindowMessage &H111, &H1E122, 0
    
         Exit For
    
        End If
    
     Next myTask
    
    
    
    Ran.Paste
    
    Ran.Select
    
    'на случай если необходимо сохранить исходный текст, yj =1
    
    'то переход в окно исходного текста
    
    If yj = 1 Then
    
      For Each myTask In Tasks
    
       If InStr(myTask.Name, "PROMT") > 0 Then
    
         myTask.SendWindowMessage &H111, &H18007, 0
    
         Exit For
    
        End If
    
     Next myTask
    
    'все выделить  
    
      For Each myTask In Tasks
    
       If InStr(myTask.Name, "PROMT") > 0 Then
    
         myTask.SendWindowMessage &H111, &H1E12A, 0
    
        End If
    
     Next myTask
    
    'скопировать в буфер обмена
    
      For Each myTask In Tasks
    
       If InStr(myTask.Name, "PROMT") > 0 Then
    
         myTask.SendWindowMessage &H111, &H1E122, 0
    
         Exit For
    
        End If
    
     Next myTask
    
    End If
    
    'PROMT накапливает буфер обмена, вот его и очищаем
    
    'может и сам очищает, но не проверял
    
    rfgt55 = rfgt55 + 1
    
    If rfgt55 > 5 Then
    
      For Each myTask In Tasks
    
       If InStr(myTask.Name, "PROMT") > 0 Then
    
         myTask.SendWindowMessage &H111, &H8513, 0
    
         Exit For
    
        End If
    
     Next myTask
    
     rfgt55 = 0
    
    End If
    
    Set Ran = Nothing
    
    End Sub
    
    
    
    
    
    ' если запустили PROMT в скрытом виде, то так можно его увидеть
    
    Private Sub PromViz()
    
     Promt.Visible = 1
    
    End Sub
    
    'а так скрыть, но не советую
    
    Private Sub PromNoViz()
    
     Promt.Visible = 0
    
    End Sub
    
    
    
    //======================================================
    
    
    
    /*Ниже приведен реализ выше написанного кода, но в C++
    
    Все - таки Word дремуче работает с памятью и лишний раз дергать его за 
    
    усы не стоит. 
    
    Могут быть накладки, в том смысле, что среди 30 файлов
    
    найти все объявления и описания, мог и пропустить, но кому надо, тот поймет*/
    
    RETPromt.cpp
    
    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    
    #include <windows.h>
    
    #include <Vcl\utilcls>
    
    
    
    #pragma hdrstop
    
    
    
    #include "SekFill.h"
    
    //---------------------------------------------------------------------------
    
    
    
    #pragma package(smart_init)
    
    
    
    
    
    extern "C" __declspec(dllexport) void __stdcall ManipPROMT()
    
    {
    
    try{
    
      if (VarIsEmpty(Stek->RetOLE()))
    
      {
    
       Stek->SetOLE1();
    
       Stek->SetOLE();
    
       EnumWindows((WNDENUMPROC)EnumWindowsProcA, 0);
    
          }
    
      else
    
          {
    
    //в отличие от ДОС выриатиа можно использовать SendMessage, т.к. сообщения поступаю все в верхнее окно
    
    SendMessage(Stek->REtHWND(), (UINT)0x111, (WPARAM)0x18007, 0);
    
    Stek->RetOLE().OlePropertySet("Text", "");
    
          }
    
      Stek->RetOLE().OleProcedure("Paste");
    
      Stek->RetOLE().OleProcedure("Translate");
    
      SendMessage(Stek->REtHWND(), (UINT)0x111, (WPARAM)0x18008, 0);
    
      SendMessage(Stek->REtHWND(), (UINT)0x111, (WPARAM)0x1E12A, 0);
    
      SendMessage(Stek->REtHWND(), (UINT)0x111, (WPARAM)0x1E122, 0);
    
      Stek->y = Stek->y + 1;
    
      if (Stek->y > 4)
    
      {
    
    SendMessage(Stek->REtHWND(), (UINT)0x111, (WPARAM)0x8513, 0);
    
    Stek->y = 0;
    
      }
    
    
    
    }
    
    catch(const Exception &E)
    
    {
    
      ShowMessage(AnsiString(E.ClassName())+ E.Message);
    
    
    
    }
    
    return;
    
    }
    
    //функция на тот случай если надо сохранить исходный текст в буфере
    
    extern "C" __declspec(dllexport) void __stdcall REtClipboard()
    
    {
    
     SendMessage(Stek->REtHWND(), (UINT)0x111, (WPARAM)0x18007, 0);
    
     SendMessage(Stek->REtHWND(), (UINT)0x111, (WPARAM)0x1E12A, 0);
    
     SendMessage(Stek->REtHWND(), (UINT)0x111, (WPARAM)0x1E122, 0);
    
    
    
    }
    
    //------------------------------------------------------------------------------------------------
    
     SekFill.h
    //---------------------------------------------------------------------------
    
    
    
    #ifndef SekFillH
    
    #define SekFillH
    
    #include <vcl.h>
    
    class TsekFill
    
    {
    
    private:
    
    //закрытые т.к. стоит CpuIdlePro для XP и часто класс открывает буфер и закрывает его
    
    //переводчик иногда глючил
    
    //объяснений нет, но сейчас перестал, а так наверно не обязательно закрывать
    
     //вариант для документа PROMT
    
     Variant V;
    
     //вариант для самого приложения PROMT
    
     Variant V1;
    
     //хэндл для основного окна
    
     HWND hwnd;
    
    public:
    
    //счетчик буфера переводчика
    
    int y;
    
    TSekFill();
    
    //возвращает интерфейс документа PROMT
    
    Variant RetOLE();
    
    //назначает интерфейс документа на вариант
    
    void SetOLE();
    
    //назначает интерфейс приложения на вариант
    
    void SetOLE1();
    
    //возвращает хэндл главного окна
    
    HWND REtHWND();
    
    //назначает хэндл на основное окно
    
    void SetHWHD(HWND&);
    
    virtual ~TSekFill();
    
    };
    
    extern TSekFill* Stek;
    
    //функция повторного вызова для определения главного окна
    
    bool CALLBACK EnumWindowsProcA(HWND, LPARAM);
    
    
    
    //---------------------------------------------------------------------------
    
    #endif
    
    
    
    //=================================================
    
     SekFill.cpp
    
    
    
    //#include <vcl.h>
    
    #include <windows.h>
    
    //#include <sysvari.h>
    
    #include <Clipbrd.hpp>
    
    #include <Vcl\utilcls>
    
    #pragma hdrstop
    
    #include "SekFill.h"
    
    
    
    
    
    //---------------------------------------------------------------------------
    
    #pragma package(smart_init)
    
    
    
    TSekFill::TSekFill()
    
    {
    
     y = 0;
    
    }
    
    
    
    //---------------------------------------------------------------------
    
     TSekFill::~TSekFill()
    
     {
    
       V = Unassigned;
    
       V1 = Unassigned;
    
     }
    
    
    
    
    
     //---------------------------------------------------------------
    
    Variant TSekFill::RetOLE()
    
    {
    
     return V;
    
    }
    
    void TSekFill::SetOLE()
    
    {
    
    V = V1.OlePropertyGet("PromtDocuments");
    
    V1.OlePropertySet("Visible", true);
    
    V = V.OleFunction("Add", 1, "PROMT.Document");
    
    }
    
    
    
    void TSekFill::SetOLE1()
    
    //коряво, но это часть класса и он распределяется в динамической памяти,
    
    //держит в памяти много параметров на время сессии Word и при любом сбое
    
    // выгружается, записывая параметры и стек посещения документов в файл,
    
    // а при загрузке восстанавливает все данные
    
    {
    
    if (VarIsEmpty(V1))
    
    {
    
      V1 = OleVariant::CreateObject("Promt.Application");
    
    }
    
    else
    
    {
    
      V1 = Unassigned;
    
      V1 = OleVariant::CreateObject("Promt.Application");
    
    }
    
    }
    
    HWND TSekFill::REtHWND()
    
    {
    
       return hwnd;
    
    }
    
    
    
    void TSekFill::SetHWHD(HWND& hWnd)
    
    {
    
       hwnd = hWnd;
    
    
    
    }
    
    
    
    bool CALLBACK EnumWindowsProcA(HWND hWnd, LPARAM lp)
    
    {
    
       char asd[60];
    
    //проверяется имя класса окна, и название окна
    
       GetClassName(hWnd, asd, 60);
    
       int ptr = strncmpi(asd, "Afx:400000:", 11);
    
       GetWindowText(hWnd, asd, 60);
    
       int ptrz = strncmpi(asd, "PROMT - [", 9);
    
       if (ptr == 0 && ptrz == 0)
    
     {
    
       Stek->SetHWHD(hWnd);
    
       return false;
    
     }
    
      return true;
    
    }

    Сергей Финкевич.
     
  2. Guest

    Guest Гость

    Репутация:
    0
    Я не понял. Ты хочешь сделать глобальные хоткеи?
     
  3. Serge

    Serge Гость

    Репутация:
    0
    <!--QuoteBegin-QUOTE+Guest-->
    <span class="vbquote">(QUOTE @ Guest)</span><!--QuoteEBegin-->Я не понял. Ты хочешь сделать глобальные хоткеи?[/quote]
    Нет, это я к тому, что справку скрывать - пошло.
     
Загрузка...
Статус темы:
Закрыта.

Поделиться этой страницей