Click here to Skip to main content
15,122,335 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Overview is that this is a project for remote support, like Teamviewer. I have a broker, client and technician app.

Currently the client app takes a screenshot and sends it to the broker. The broker realises its a screenshot and sends it to the relevant technician client that authenticated prior.

Technician client then receives the image from the broker and displays it in a picturebox at the Technician end.

Issue is that when the screenshot is sent and displayed once, the client crashes, but i'm not getting any kind of exception so i'm lost and looking for help. I expect it's something stupid / simple, but i cannot see what.

Image Example - You will see client is connected to the broker in the Technician app, but on the broker app it has crashed and vanished.

[^]

Below is the relevant code:

Client:
Note: I also get the amount of screens and current screen resolution and send with the screenshot for use at the technician end.
C#
private int      ScreenToView            =   0;
private bool     SendMyScreen            =   false; //Set to true upon technician connection
private bool     ShowCursor              =   false;
public void SendScreen( )
        {
            //Holds the number of screens on the client machine
            int NumOfScreens = 0;

            while( SendMyScreen )
            {
                try
                {
                    //Get SS based on screen the technician are viewing
                    Bitmap bmpScreenshot = new Bitmap( Screen.AllScreens[ ScreenToView ].Bounds.Width, Screen.AllScreens[ ScreenToView ].Bounds.Height, PixelFormat.Format16bppRgb555 );
                    using Graphics gfxScreenshot = Graphics.FromImage( bmpScreenshot );
                    gfxScreenshot.CopyFromScreen( Screen.AllScreens[ ScreenToView ].Bounds.X, Screen.AllScreens[ ScreenToView ].Bounds.Y, 0, 0, Screen.AllScreens[ ScreenToView ].Bounds.Size, CopyPixelOperation.SourceCopy );
                        
                    if( ShowCursor )
                    {
                        CURSORINFO pci;
                        pci.cbSize = Marshal.SizeOf( typeof( CURSORINFO ) );

                        if( GetCursorInfo( out pci ) )
                        {
                            DrawIcon( gfxScreenshot.GetHdc( ), pci.ptScreenPos.x, pci.ptScreenPos.y, pci.hCursor );
                            gfxScreenshot.ReleaseHdc( );
                        }
                    }

                    //Save the screenshot into memory
                    using MemoryStream ms   =   new MemoryStream( );
                    bmpScreenshot.Save( ms, ImageFormat.Jpeg );

                    //Create our buffer based on memory length
                    byte[ ] ssBuffer = new byte[ ms.Length + 11 ];

                    //Set the command to screenshot
                    ssBuffer[ 0 ] = 2;

                    //Number of screens
                    foreach( Screen screen in Screen.AllScreens ) { NumOfScreens++; }
                    Array.Copy( BitConverter.GetBytes( NumOfScreens ), 0, ssBuffer, 1, 1 );

                    //Screen width & height
                    Array.Copy( BitConverter.GetBytes( Screen.AllScreens[ ScreenToView ].Bounds.Width ), 0, ssBuffer, 2, 4 );
                    Array.Copy( BitConverter.GetBytes( Screen.AllScreens[ ScreenToView ].Bounds.Height ), 0, ssBuffer, 6, 4 );

                    //Copy the screenshot into buffer at index 10 as 0-9 are info
                    Array.Copy( ms.ToArray( ), 0, ssBuffer, 10, ms.Length );

                    //Send Screen info to Broker
                    BrokerSocket.Send( ssBuffer, Convert.ToInt32( ms.Length + 10 ), SocketFlags.None );

                    //Reset screen number
                    NumOfScreens = 0;

                    //Wait before sending next
                    Thread.Sleep( 100 );
                }
                catch( Exception e )
                {
                    DebugLog( "SendScreen Exception: " + e.Message.ToString( ) );
                }
            }
        }


Broker:
C#
public void SendDataToTechnician( Socket clientSocket, byte[ ] data, int bytesRead )
        {
            try
            {
                if( Success.technicianSocket != null && Success.technicianSocket.Connected )
                {
                    Success.technicianSocket.Send( data );
                }
            }
            catch( Exception ex ) { DebugLog( "SendDataToTechnician Exception: " + ex.Message.ToString( ) ); }
        }


Technician:
C#
public void ShowClientScreen( byte[ ] data, int dataRead )
        {
            try
            {
                //Create a new byte array
                byte[ ] imgData         =   new byte[ dataRead ];

                //Get Number Of Remote Screens
                byte[ ] NumOfScreens    =   new byte[ 4 ]; Array.Copy( data, 1, NumOfScreens, 0, 1 );
                Remote_Num_Of_Screens   =   BitConverter.ToInt32( NumOfScreens, 0 );

                //Get Remote Screen Resolution from array
                byte[ ] ScreenW         =   new byte[ 4 ]; Array.Copy( data, 2, ScreenW, 0, 4 );
                byte[ ] ScreenH         =   new byte[ 4 ]; Array.Copy( data, 6, ScreenH, 0, 4 );
                Remote_Screen_Width     =   BitConverter.ToInt32( ScreenW, 0 );
                Remote_Screen_Height    =   BitConverter.ToInt32( ScreenH, 0 );
             
                //Copy the largeBuffer into our temporary byte array from array 1 as 0 contains the command
                //Subtract 9 as we received 10 in information
                Array.Copy( data, 10, imgData, 0, dataRead -9 );

                //Using memorystream - Same method of how it was sent
                using MemoryStream ms = new MemoryStream( imgData );

                //Set Image
                Image RecvImage = Image.FromStream( ms );

                //Sets the image to be stretched into the picture window no mater it's size
                if( ScreenSize == PictureBoxSizeMode.StretchImage )
                {
                    MethodInvoker ChangeScreenImageTypeStretch = delegate
                    {
                        pictureBox1.SizeMode    =   PictureBoxSizeMode.StretchImage;
                        pictureBox1.Image       =   RecvImage;
                    };
                    Invoke( ChangeScreenImageTypeStretch );
                }
                else if( ScreenSize == PictureBoxSizeMode.Normal )
                {
                    MethodInvoker ChangeScreenImageTypeAutoSize = delegate
                    {
                        pictureBox1.SizeMode    =   PictureBoxSizeMode.Normal;
                        pictureBox1.Image       =   RecvImage;
                    };
                    Invoke( ChangeScreenImageTypeAutoSize );
                }
            
                //Invoke btnScreenNum. Invoke here as it was created on another thread
                MethodInvoker UpdateScreenNumButton = delegate { btnScreenNum.Text = Remote_Screen_Num + 1 + "/" + Remote_Num_Of_Screens; };
                Invoke( UpdateScreenNumButton );
            }
            catch( Exception ex )
            {
                DebugLog( "ShowClientScreen Exception: " + ex.Message.ToString( ) );
            }
        }


What I have tried:

I've logged the screenshot buffer which seems to be around 65535 so confirmed the buffer size if plenty.

Logged the information at the technician end and all seems fine
Posted
Updated 21-Feb-20 6:08am
Comments
OriginalGriff 17-Feb-20 2:31am
   
And? "It's crashed" is no information - it doesn't tell us anything about what happened, and we can't run your code in isolation to try and find out - we have no idea about the wider context of the app(s) and what threads they are using.

So what have you done to find out what is going on? What does the debugger show is happening? Have you tried using it? What messages do you get? what do your logs show?

This isn't a problem that we can say "it's that bit" just from a casual read of the code - it's going to take a good understanding of what you are trying to do and how that fits into the whole system.
Gerry Schmitz 17-Feb-20 14:21pm
   
You start with a bare minimum prototype that sends and receives a small pic, and go from there. You got too much going on otherwise.
lmoelleb 18-Feb-20 2:44am
   
Maybe an unhandled exception? https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.unhandledexception?view=netframework-4.8
And your using statement only works as expected with C# 8, so check you are not compiling with an older language version.
By the way, 65535 is rather suspicious. This is the maximum length that can be represented with 16 bits and extremely small for a screenshot. Not sure where you see it is 65535, but I would doublecheck it.
Richard Deeming 18-Feb-20 13:58pm
   
foreach( Screen screen in Screen.AllScreens ) { NumOfScreens++; }

NB: The Screen.AllScreens property[^] returns an array. You can replace that loop with:
NumOfScreens = Screen.AllScreens.Length;
sjsteve33171 21-Feb-20 9:11am
   
Thats handy, thank you!
sjsteve33171 21-Feb-20 10:03am
   
Thank you all for the comments so far. I've spent a while looking into it and found out it's an issue with the app.manifest file.

Basically I noticed when using anything on the screen scaling over 100%, e.g. 150% or 200% i would get an image that was too big for the technician to see and it wouldn't fit. Put it to 100% and it works fine. So i found an article which explained to add the following to the app.manifest

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
  </windowsSettings>
</application>


Clearly this is causing the issue. If i remove it, it's fine and works. Starting to look around for ideas but if anyone can steer me in the direction of handling high DPI in a screenshot / app i'd appreciate it.

1 solution

using Graphics gfxScreenshot = Graphics.FromImage( bmpScreenshot );

using MemoryStream ms = new MemoryStream( imgData );

These "usings" are being "misused". You "wrap" it around code that requires it's scope.

Read up on "using" and start a new question if you need.
   
Comments
sjsteve33171 21-Feb-20 15:50pm
   
I did that previously. Visual Studio is telling me to simplify it by doing that
Gerry Schmitz 21-Feb-20 19:51pm
   
You're right, I'm out of date.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900