Вопрос по WinAPI

 

Здравствуйте.

Появились задачи, которые без WinAPI не решить. Стал разбираться как это работает. Частично разобрался, но несколько не решённых вопросов не дают продвинуться дальше в моих задачах.

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

Имена кнопок "Сохранить" и "Отмена" получается поменять текст, а в полях в которых нужно менять ничего не меняется.


Делаю так

#include <WinAPI\WinAPI.mqh>
#define GA_ROOT 2
#define WM_LBUTTONDOWN 0x0201
#define WM_COMMAND     0x0111

#define VK_DOWN        0x28
#define VK_RETURN      0x0D //ENTER key
#define WM_KEYDOWN     0x0100
#define VK_TAB         0x09 //TAB key
#define BM_CLICK       0x000000F5
#define WM_MENUSELECT  0x011F

HANDLE Handle;
//+------------------------------------------------------------------+
int OnInit()
  {
   Handle = GetAncestor(ChartGetInteger(0, CHART_WINDOW_HANDLE), GA_ROOT);//хэндл главного окна
   int ControlID[] = {0xE81E, 0x804E, 0x2712}; // Spy++ ControlID, (0xE81E это терминал)(0x804E это тестер стратегий), (0x2712 это строка - обзор, настройки, параметры,агенты..)
   HANDLE handleID = GetHandle(ControlID);
   SendMessageW(handleID, WM_LBUTTONDOWN, 1, 0x000A00AD);//выбрали вкладку "Параметры",  ControlID 0x000A00AD кнопки "Параметры"

   PostMessageW(Handle, WM_COMMAND, 33159, 0);// открыли форму сохранения файла
   Sleep(1000);

   handleID = GetLastActivePopup(Handle);  // нашли форму сохранения файла

   SetWindowTextW(GetDlgItem(handleID, 2), "File Отмена"); // ввели имя файла
   SetDlgItemTextW(handleID, 1, "File Сохранить");
   SetDlgItemTextW(handleID, 0x3E9, "File.set");

 return(INIT_FAILED);
}
//+------------------------------------------------------------------+
long GetHandle(const int &ControlID[])
  {
   long handleID = GetAncestor(ChartGetInteger(0, CHART_WINDOW_HANDLE), GA_ROOT);
   const int Size = ArraySize(ControlID);

   for(int i = 0; i < Size; i++)
      handleID = GetDlgItem(handleID, ControlID[i]);

   return(handleID);
  }


Spy++ показывает, что ControlID строки с именем файла 0x3E9, но так ничего не меняется, хотя ControlID кнопок 1 и 2 текст на кнопках меняется.


Что не так?

Как поменять название файла?


Затем если допустим здесь всё получилось, но файл с таким именем уже есть, то выскакивает окно с вопросом "Заменить файл ?"   "ДА"  "Нет".  

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

Это работает

// Закрыть окно
   Handle = GetAncestor(ChartGetInteger(0, CHART_WINDOW_HANDLE), GA_ROOT);//хэндл главного окна
   handleID = GetLastActivePopup(Handle);  // нашли форму сохранения файла
   HANDLE handleDlg;
   handleDlg = GetDlgItem(handleID, 1);                //определяем дескриптор кнопки "Сохранить"
   PostMessageW(handleID, WM_COMMAND, 1, handleDlg);   //нажимаем на кнопку 

а вот дальше если всплыло окно  "Заменить файл ?"   "ДА"  "Нет"   ничего не работает

//--- это не работает
   handleID = GetLastActivePopup(Handle);              //  получаем дескриптор всплывающего окна СОХРАНЯТЬ ИЛИ НЕТ

   SendMessageW(handleID, WM_LBUTTONDOWN, 1, 0x000C001C);
   SendMessageW(handleID, WM_KEYDOWN, VK_TAB, 0x000F0001);
   PostMessageW(handleID, WM_KEYDOWN, VK_DOWN, 1);
   SendMessageW(handleID, WM_KEYDOWN, VK_RETURN, 0x001C001);

и так тоже не работает

   handleDlg = GetDlgItem(handleID, 0);

   SendMessageW(handleDlg, WM_LBUTTONDOWN, 1, 0x000C001C);
   SendMessageW(handleDlg, WM_KEYDOWN, VK_TAB, 0x000F0001);
   PostMessageW(handleDlg, WM_KEYDOWN, VK_DOWN, 1);
   SendMessageW(handleDlg, WM_KEYDOWN, VK_RETURN, 0x001C001);

Spy++ выдаёт это  0x000C001C как Lparam при нажатии кнопки Inter и  0x000F0001  Lparam при нажатии кнопки Tab

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

Как нажать на кнопку "Да" ???

Файлы:
Effort.mq5  7 kb
 

Тут только методом проб и ошибок пытаться что-то менять. Пробовали вот так поменять имя файла?

SetWindowTextW(GetDlgItem(handleID, 0x3E9), "имя файла");

Также нужно попробовать развернуть саму задачу. Если речь о том, чтобы автоматически сохранять отчеты и файлы настроек, то, по-моему, что-то такое уже было у fxsaber, причем без WinAPI.

 
Ihor Herasko #:

Тут только методом проб и ошибок пытаться что-то менять. Пробовали вот так поменять имя файла?

Также нужно попробовать развернуть саму задачу. Если речь о том, чтобы автоматически сохранять отчеты и файлы настроек, то, по-моему, что-то такое уже было у fxsaber, причем без WinAPI.

Та уже неделю методом проб и ошибок... В итоге всё таки решился спросить на форуме. 

То, что вы написали в моём коде уже есть, немного другая функция, но смысл тот же,  и оно не работает.

SetDlgItemTextW(handleID, 0x3E9, "File.set");

Я весь форум просмотрел по тегам  WinAPI, PostMessageA,  PostMessageW, Spy++, winuser32, user32.dll  и подобные, всё что есть на форуме я прочитал, публикации  fxsaber тоже все просмотрел. Пока ответа не нашёл. 

В инете тоже ничего подобного найти не могу, везде написано, нади ControlID и всё получится, а здесь в одном месте просто не получается, а в другом ControlID равен нулю :)

 
Aleksandr Slavskii #:

Та уже неделю методом проб и ошибок... В итоге всё таки решился спросить на форуме. 

То, что вы написали в моём коде уже есть, немного другая функция, но смысл тот же,  и оно не работает.

Поле имени - это ComboBox. По идее, к нему должен быть доступ при помощи SetWindowText(). Посмотрите, что возвращает 

GetDlgItem(handleID, 0x3E9)

Будет ли это значение совпадать с дескриптором окна, которое видите в итоге через Spy++? Думаю, именно тут проблема.

Если же дескриптор правильный, то можно попробовать "усугубить": добавить собственный вариант выбора в список при помощи сообщения CB_ADDSTRING с последующим выбором этого варианта в списке (CB_SETCURSEL).

P. S. Помню, что с ComboBox не всегда срабатывало чтение через GetWindowText(). Оказалось, что нужно использовать сообщение CB_GETLBTEXT. Посмотрел, нет ли чего-то подобного для установки строки. Нет. 

 

Тут засада в чём то другом. Я вообще хэндл этой строки найти не могу.

Сделал так

   Handle = GetAncestor(ChartGetInteger(0, CHART_WINDOW_HANDLE), GA_ROOT);
   handleID = GetLastActivePopup(Handle);
   for(int i = 0; i < 1000000000; i++)
     {
      HANDLE aaa = GetDlgItem(handleID, i);
      if(aaa > 0)
        {
         Print("aaa = " + (string)aaa + ", i =  " + (string)i);
         SendMessageW(aaa, WM_CLEAR, 0, 0);
         SetWindowTextW(aaa, "File.set");
         Sleep(100);
         for(int j = 0; j < 1000000000; j++)
           {
            HANDLE sss = GetDlgItem(aaa, j);
            if(sss > 0)
              {
               Print("sss = " + (string)sss + ",  i = " + (string)i + ", j = " + (string)j);
               SendMessageW(aaa, WM_CLEAR, 0, 0);
               SetWindowTextW(aaa, "File.set");
               Sleep(100);
              }
           }
        }
     }

Вот всего хэндлов в этом окне мне выдало. При этом хэндла со строкой ввода имени файла, здесь нет.

Где его искать?


i =1 изменило текст на кнопке "Сохранить", i = 2 изменило текст на кнопке "Отмена".   Это значит, что сканировал я, нужное окно.

 
Ihor Herasko #:

Поле имени - это ComboBox. По идее, к нему должен быть доступ при помощи SetWindowText(). Посмотрите, что возвращает 

GetDlgItem(handleID, 0x3E9)

Возвращает ноль  :(

 
Вам же шпион всё показал. Комбобокс - не прямой потомок диалога, там прокладок 3 штуки. Вам нужно прогуляться по дереву дочерних окон и найти такое окно с классом "ComboBox", у которого есть дочернее окно с классом "Edit". Это и будет комбобокс имени файла. Вот ему и можно будет посылать WM_SETTEXT.
 
SeriousRacoon #:
Вам же шпион всё показал. Комбобокс - не прямой потомок диалога, там прокладок 3 штуки. Вам нужно прогуляться по дереву дочерних окон и найти такое окно с классом "ComboBox", у которого есть дочернее окно с классом "Edit". Это и будет комбобокс имени файла. Вот ему и можно будет посылать WM_SETTEXT.

Я понял, что нужно прогуляться по дочерним окнам, но я знаю для этого, только одну функцию GetDlgItem, а она не хочет на прогулке находить хэндлы потомков. Ей же нужно подсунуть ControlID дочернего окна, а где его взять если они все в этой ветви равны нулю?

 
SeriousRacoon #:
Вам же шпион всё показал. Комбобокс - не прямой потомок диалога, там прокладок 3 штуки. Вам нужно прогуляться по дереву дочерних окон и найти такое окно с классом "ComboBox", у которого есть дочернее окно с классом "Edit". Это и будет комбобокс имени файла. Вот ему и можно будет посылать WM_SETTEXT.

Вай!!вах!!! Получилось!!!!, Чего то получилось!!!  Спасибо, буду дальше разбираться. 

Я не подумал, что можно сделать так:

   Handle = GetAncestor(ChartGetInteger(0, CHART_WINDOW_HANDLE), GA_ROOT);//хэндл главного окна
   handleID = GetLastActivePopup(Handle);  // нашли форму сохранения файла
   HANDLE sss;
   while(handleID != 0)
     {
      handleID = GetDlgItem(handleID, 0);
      if(handleID != 0)
         sss = handleID;
     }
   handleID = GetDlgItem(sss, 0x3E9);

А оказывается можно! )))


Спасибо. Всем спасибо!

 
Aleksandr Slavskii #:

Вай!!вах!!! Получилось!!!!, Чего то получилось!!!  Спасибо, буду дальше разбираться. 

Я не подумал, что можно сделать так:

А оказывается можно! )))


Спасибо. Всем спасибо!

Ну да, надо всё в цикле обходить. Так оно будет работать, но не на всех виндовсах - прокладки и их число меняется. Обычно растёт )


Лучше как-то так, не проверял в работе, но идея ясна, думаю.


HWND FindThatFuckingCombo(HWND hParent)
{
        HWND hResult = NULL;

        for (HWND hCtl = ::GetWindow(hParent, GW_CHILD);
                 hCtl != NULL && hResult == NULL;
                 hCtl = ::GetWindow(hCtl, GW_HWNDNEXT))
        {
                TCHAR szClassName[128];
                if (GetClassName(hCtl, szClassName, 128) && lstrcmp(szClassName, _T("ComboBox")) == 0)
                {
                        HWND hFirstChild = ::GetWindow(hCtl, GW_CHILD);
                        if (hFirstChild && GetClassName(hFirstChild, szClassName, 128) && lstrcmp(szClassName, _T("Edit")) == 0)
                        {
                                return hCtl;
                        }
                }

                hResult = FindThatFuckingCombo(hCtl);
        }

        return hResult;
}
 
SeriousRacoon #:

Ну да, надо всё в цикле обходить. Так оно будет работать, но не на всех виндовсах - прокладки и их число меняется. Обычно растёт )

Лучше как-то так, не проверял в работе, но идея ясна, думаю.


Спасибо, попробую разобраться. Я пытался использовать GetClassName, но так и не смог понять, с чем его едят)  С вашим примером будет проще.

Причина обращения: