views:

165

answers:

1

I have customer that uses older, custom built ERP application for which they don't have source code and company that developed it does not exist any more. Application is developed in 2000 and it's built with Delphi. Since last executable is from 2003, it could be D6 or D7.

On one important form there are some fields where customer would like to display additional data from other databases and asked me if it's possible to "duct tape" data over existing form.

First idea I got is to build application that will:

  • browse list of windows target application creates and find controls on form
  • attach "some" event when to get notified when target form is displayed
  • attach "some" event when to get notified when field on target form is changed
  • display additional information in small window overlaying target form

Are there any examples on how to do something like this. I searched google with variations of this question title, but without success.

Note - rewriting of ERP application is not planned.

About language - I can do it with C# or Delphi.

+2  A: 

I'm going to answer this from a purely C & Win32 point of view, because I don't know Delphi or its libraries. Converting this to C# can be accomplished via p/invoke, but some parts may/will need to be unmanaged.

First off, no guarantees. If the target application is doing windows-less controls (if there isn't an HWND under every on-screen control) you're pretty much out of luck. This isn't all that uncommon, so yeah...

Step 1, register a window hook listening for new windows created by the target process*:

//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) should stash the HWND away if the correct process (determined via GetWindowThreadProcessId) owns it. At this point, you'll be able to get every top-level window owned by the target process. Note that registering a global hook carries a notable performance penalty, not that it really matters in your case but you should expect it.

Now for the fun part. There's no reliable way to tell when a window is fully constructed, or when its done rendering (there are ways to tell when it STARTS rendering, but that doesn't really help). My advice, guess. Just throw some arbitrary wait in there and then try and enumerate all the child windows.

To enumerate child windows (if you know enough about the target window, there are better ways to do this; but I'm assuming a search is easiest):

//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;
}

Implementing IsTargetWindow is another tricky part. Hopefully you'll find some reliable test for doing so (like checking the class name, window name, style, something; look at GetWindowInfo).

Once you have the window you want to monitor, you can use SetWindowLongPtr and GWLP_WNDPROC to watch all messages it receives. This will require code injection (and thus unmanaged code) and is awfully low level. I'd advise against it if you could possibly avoid it, but lacking the source...

I think this answers is a decent starting point, but once again this is going to be incredibly painful if its even possible at all. Good luck.

*Alternatively, if you know that the target app doesn't create windows except at startup (or at detectable/predictable points in time) you can use EnumWindows.

Kevin Montrose
Kevin, thank you very much for this very detailed answer.
zendar