Sorry for the delay. Some thoughts on client/servers.
The client always controls the interaction. This means that the server is silent until contacted by the client (the server is actually in some form of listening state). This means that your use of a push is wrong. There should be no timer on the server side. Only when the client connects to the server should the server prepare and send the data.
On the client side, I'd suggest that you initially not use a timer. It just confuses the debugging process. Rather each time that a "connect" button is clicked, the client connects to the server and awaits the server's response.
This is the classic client/server paradigm.
You are collecting an enormous amount of data. My monitor screen resolution is 1920x1080. That's 8294400 bytes that need to be transmitted. Since you have chosen TCP (rather than UDP), the image requires about 5525 packets to cross from the server to the client (assuming an MTU of 1400 bytes, a nominal value). At a bandwidth of 16mbit/sec, that amount of data will take (optimally) more than four seconds. So your timer must be set to greater than that value. If you follow my recommendation that server only responds when contacted, then the client can adjust its connect timing to account for the transit time. See maximum packet size for a TCP connection[^] for a discussion of MTU.
If you can concentrate on only one window on the desktop, there are ways in which an image of that window can be captured. That will significantly reduce that amount of data you must transmit. The following code performs the window to image capture.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
public class CaptureDesktopWindow
{
const int SRCCOPY = 0x00CC0020;
[ StructLayout ( LayoutKind.Sequential ) ]
struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[ DllImport ( "gdi32.dll" ) ]
static extern bool BitBlt ( IntPtr hObject,
int nXDest,
int nYDest,
int nWidth,
int nHeight,
IntPtr hObjectSource,
int nXSrc,
int nYSrc,
int dwRop );
[ DllImport ( "gdi32.dll" ) ]
static extern IntPtr CreateCompatibleBitmap ( IntPtr hDC,
int nWidth,
int nHeight );
[ DllImport ( "gdi32.dll" ) ]
static extern IntPtr CreateCompatibleDC ( IntPtr hDC );
[ DllImport ( "gdi32.dll" ) ]
static extern bool DeleteDC ( IntPtr hDC );
[ DllImport ( "gdi32.dll" ) ]
static extern bool DeleteObject ( IntPtr hObject );
[ DllImport ( "user32.dll" ) ]
static extern IntPtr GetWindowDC ( IntPtr hWnd );
[ DllImport ( "user32.dll" ) ]
static extern IntPtr GetWindowRect ( IntPtr hWnd,
ref RECT rect );
[ DllImport ( "user32.dll" ) ]
static extern IntPtr ReleaseDC ( IntPtr hWnd,
IntPtr hDC );
[ DllImport ( "gdi32.dll" ) ]
static extern IntPtr SelectObject ( IntPtr hDC,
IntPtr hObject );
public static Image window_image ( IntPtr handle )
{
IntPtr hBitmap = IntPtr.Zero;
IntPtr hdcDest = IntPtr.Zero;
IntPtr hdcSrc = IntPtr.Zero;
IntPtr hOld = IntPtr.Zero;
int height = 0;
Image image = null;
int width = 0;
RECT windowRect;
try
{
hdcSrc = GetWindowDC ( handle );
windowRect = new RECT ( );
GetWindowRect ( handle, ref windowRect );
width = windowRect.right - windowRect.left;
height = windowRect.bottom - windowRect.top;
hdcDest = CreateCompatibleDC ( hdcSrc );
hBitmap = CreateCompatibleBitmap ( hdcSrc,
width,
height );
hOld = SelectObject ( hdcDest, hBitmap );
BitBlt ( hdcDest,
0,
0,
width,
height,
hdcSrc,
0,
0,
SRCCOPY );
SelectObject ( hdcDest, hOld );
image = Image.FromHbitmap ( hBitmap );
}
catch
{
image = null;
}
finally
{
if ( hdcDest != IntPtr.Zero );
{
DeleteDC ( hdcDest );
}
if ( hdcSrc != IntPtr.Zero )
{
ReleaseDC ( handle, hdcSrc );
}
if ( hBitmap != IntPtr.Zero )
{
DeleteObject ( hBitmap );
}
}
return ( image );
}
public static Image capture_window_image ( string title )
{
Image image = null;
IntPtr window_handle = IntPtr.Zero;
foreach ( Process process in Process.GetProcesses ( ) )
{
if ( process.MainWindowTitle.
ToLower ( ).
Trim ( ).
StartsWith ( title.ToLower ( ).
Trim ( ) ) )
{
window_handle = process.MainWindowHandle;
break;
}
}
if ( window_handle != IntPtr.Zero )
{
image = window_image ( window_handle );
}
return ( image );
}
}
It is invoked by
Image image = CaptureDesktopWindow.capture_window_image ( "Dynamic Update" );
In this case, there is a window titled "Dynamic Update" on the desktop.
Hope that helps.