There are no nice ways to do this, but one approach that may work for you is to hook the application in question using SetWindowsHookEx(...) to add a GetMsgProc, which draws your overlay in response to WM_PAINT messages. The basic idea is that you're drawing YOUR graphics right after the application finishes its own drawing.
In your main app:
....
HMODULE hDllInstance = LoadLibrary("myFavoriteDll");
HOOKPROC pOverlayHook = (HOOKPROC)GetProcAddress(hDllInstance, "OverlayHook");
SetWindowsHookEx(WH_GETMESSAGE, pOverlayHook, hDllInstance, threadId);
Off in a DLL somewhere:
LRESULT CALLBACK OverlayHook(int code, WPARAM wParam, LPARAM lParam)
{
//Try and be the LAST responder to WM_PAINT messages;
//Of course, if some other application tries this all bets are off
LRESULT retCode = CallNextHookEx(NULL, code, wParam, lParam);
//Per GetMsgProc documentation, don't do anything fancy
if(code < 0) return retCode;
//Assumes that target application only draws when WM_PAINT message is
//removed from input queue.
if(wParam == PM_NOREMOVE) return retCode;
MSG* message = (MSG*)lParam;
//Ignore everything that isn't a paint request
if(message->message != WM_PAINT) return retCode;
PAINTSTRUCT psPaint;
BeginPaint(message->hwnd, &psPaint);
//Draw your overlay here
...
EndPaint(message->hwnd, &psPaint);
return retCode;
}
This is all win32 so your C# code will be p/invoke heavy and correspondingly quite ugly. Your DLL must be unmanaged as well (if you intend to inject into a process other than your own), making this an even nastier solution.
This would solve your issue with z-order and clipping issues, as you're rendering into the window itself. However, if the application you're targeting does any drawing outside of the WinProc responding to WM_PAINT things fall apart; this is not an entirely uncommon occurence.