Прикрепить окно к запущенному приложению

У меня есть клиент, который использует старое, специально разработанное приложение ERP, для которого у него нет исходного кода, и компания, которая его разработала, больше не существует. Приложение разработано в 2000 году и построено на Delphi. Так как последний исполняемый файл с 2003 года, это может быть D6 или D7.

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

Первая идея, которую я получил, - создать приложение, которое будет:

  • просмотреть список окон, которые создает целевое приложение, и найти элементы управления в форме
  • прикрепить "некоторое" событие, когда получать уведомление при отображении целевой формы
  • прикрепить "некоторое" событие, когда получать уведомление при изменении поля в целевой форме
  • отображать дополнительную информацию в небольшом окне с наложением целевой формы

Есть ли примеры, как сделать что-то подобное? Я искал в Google варианты этого названия вопроса, но безуспешно.

Примечание - переписывание приложения ERP не планируется.

Насчет языка - я могу сделать это с C# или Delphi.

1 ответ

Решение

Я собираюсь ответить на это с чисто C & Win32 точки зрения, потому что я не знаю Delphi или его библиотек. Преобразование этого в C# может быть выполнено через p/invoke, но некоторые части могут / должны быть неуправляемыми.

Во-первых, никаких гарантий. Если целевое приложение выполняет элементы управления без окон (если нет HWND под каждым контролем на экране) вам почти не повезло. Это не так уж редко, так что да...

Шаг 1, зарегистрируйте обработчик окна для прослушивания новых окон, созданных целевым процессом *:

//dllHMod is an HMODULE that refers to the DLL containing ShellHookProc
HHOOK hook = SetWindowsHookEx(WH_SHELL, ShellHookProc, dllHMod, 0);
// error handling, stashing hook away for unregistering later, etc...

LRESULT CALLBACK ShellHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
  if(nCode < 0) return CallNextHookEx(NULL, nCode, wParam, lParam);

  if(nCode == HSHELL_WINDOWCREATED)
  {
    WindowCreate((HWND)wParam);
  }

  return 0;
}

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

Теперь самое интересное. Не существует надежного способа определить, когда окно полностью построено или когда оно выполнено рендерингом (есть способы узнать, когда он начинает рендеринг, но это не очень помогает). Мой совет, угадай. Просто добавьте произвольное ожидание и попробуйте перечислить все дочерние окна.

Чтобы перечислить дочерние окна (если вы достаточно знаете о целевом окне, есть лучшие способы сделать это; но я предполагаю, что поиск проще всего):

//targetHWND is an HWND of one of the top-level windows you've found
EnumChildWindows(targetHWND, ChildWindowCallback, NULL);
//more code...

BOOL ChildWindowCallback(HWND window, LPARAM ignored)
{
  if(IsTargetWindow(window)) { /* Do something */ }

  return TRUE;
}

Внедрение IsTargetWindow это еще одна сложная часть. Надеюсь, вы найдете надежный тест для этого (например, проверка имени класса, имени окна, стиля, чего-нибудь; посмотрите на GetWindowInfo).

Если у вас есть окно, которое вы хотите контролировать, вы можете использовать SetWindowLongPtr а также GWLP_WNDPROC смотреть все сообщения, которые он получает. Это потребует внедрения кода (и, следовательно, неуправляемого кода) и является ужасно низким уровнем. Я бы посоветовал против этого, если бы вы могли избежать этого, но не хватает источника...

Я думаю, что эти ответы являются хорошей отправной точкой, но еще раз это будет невероятно болезненным, если вообще возможно. Удачи.

* В качестве альтернативы, если вы знаете, что целевое приложение не создает окна, кроме как при запуске (или в обнаруживаемые / предсказуемые моменты времени), вы можете использовать EnumWindows,

Другие вопросы по тегам