0

Problem

If I display an image in an element it will be 3 out of 10 times a incorrect pixel layout for a wallpaper and sometimes a wrong pixel layout for a print screen received from a socket. It looks like the last rows of pixels are not filled in correctly.

enter image description here

Situation

I have a socket server who will send a request to an client. The client than send the image back to the server. If it is received the image will be shown in a UserControl to an <Image /> element. The images that the server can request are Print screen and a wallpaper. The wallpaper will give a corrupted image often and print screen sometimes.

I use TCP for the socket protocol. If the server receives the image the server will set the source to the <Image/> element. See Example:

enter image description here

My thoughts:

  1. It could be a conversion problem, because I first covert a image to bytes[] than I get the Image source and convert it back to a Bitmap Source.
  2. It could be a problem from how I construct the print screen or wallpaper. But the wallpaper will be corrupted often and the print screen rarely, the print screen did be corrupted often in the past. So it does not seem obvious to me that these classes will be the problem.
  3. It is not a socket problem I use TCP so the image should arrive completely.

I use for both images the same conversion, bitmap to bytes -> bytes to BitmapSourse -> BitmapSource to <Image /> element.Source.

If the image is corrupted and I safe the image "which is an option in my program" the saved image will also be corrupted. I safe the same BitmapSource to an .png So the problem should probably be in the BitmapSource.

Question

Does anyone know why this image is not displaying well sometimes like the first image in my example. Or maybe a better way for displaying an image on a WPF user control.

Thanks in advance.

Code I use

Notice!! the code for getting a wallpaper or print screen need to work in a console application some ways of getting these images can not be done in console. I found these two classes that did work.

    // XAML code where the image is shown.
    <Image x:Name="PrintScreen" Grid.Column="1" Height="Auto" Grid.Row="3" Width="Auto" Grid.ColumnSpan="2" StretchDirection="DownOnly" />

    // set bitmap source to image element
    public void SetPrintscreen(BitmapSource source)
    {
        Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
        {
            // PrintScreen = Image element
            PrintScreen.Source = source;
        }));
    }

    // Converts Bitmap to byte[]
    public static byte[] ImageToByte(Bitmap img)
    {
        var converter = new System.Drawing.ImageConverter();
        return (byte[]) converter.ConvertTo(img, typeof(byte[]));
    }

    // Converts byte to bitmap source
    public static BitmapSource BytesToBitmapSource(byte[] data)
    {
        try
        {
            return (BitmapSource) new ImageSourceConverter().ConvertFrom(data);
        }
        catch (Exception ex)
        {
            Logger.ErrorLog.WriteException(ex);
            return BitmapToSource(EmtyBitmap());
        }
    }


    //=========== Class that I use for getting the walpaper ================
  internal class WallpaperManager
  {
    private const uint SpiGetdeskwallpaper = 0x73;
    private const int SpiSetdeskwallpaper = 20;
    private const int SpifUpdateinifile = 0x01;
    private const int SpifSendwininichange = 0x02;
    private const int MaxPath = 260;

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SystemParametersInfo(uint uAction, int uParam, string lpvParam, int fuWinIni);

    public Image GetWallpaper()
    {
        var currentWallpaper = new string('\0', MaxPath);
        SystemParametersInfo(SpiGetdeskwallpaper, currentWallpaper.Length, currentWallpaper, 0);
        return Image.FromFile(currentWallpaper.Substring(0, currentWallpaper.IndexOf('\0')));
    }

    public void SetWallpaper(byte[] data, string extention, Style style)
    {
        var tempPath = Path.Combine(Path.GetTempPath(), "wallpaper" + extention);

        using (var ms = new MemoryStream(data))
        {
            using (var bitmap = new Bitmap(ms))
            {
                bitmap.Save(tempPath);
            }
        }

        var key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true);

        switch (style)
        {
            case Style.Stretched:
                key.SetValue(@"WallpaperStyle", 2.ToString());
                key.SetValue(@"TileWallpaper", 0.ToString());
                break;
            case Style.Centered:
                key.SetValue(@"WallpaperStyle", 1.ToString());
                key.SetValue(@"TileWallpaper", 0.ToString());
                break;
            case Style.Tiled:
                key.SetValue(@"WallpaperStyle", 1.ToString());
                key.SetValue(@"TileWallpaper", 1.ToString());
                break;
        }

        SystemParametersInfo(SpiSetdeskwallpaper, 0, tempPath, SpifUpdateinifile | SpifSendwininichange);
    }


    // ================ Class I use for getting print screen =============
     internal static class ScreenCapturer
{
    public static Point CursorPosition;

    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern IntPtr GetDesktopWindow();

    /// <summary> Capture the active window (default) or the desktop and return it as a bitmap </summary>
    /// <param name="mode">Optional. The default value is CaptureMode.Window.</param>
    public static Bitmap Capture(CaptureMode mode = CaptureMode.Window)
    {
        return Capture(mode == CaptureMode.Screen ? GetDesktopWindow() : GetForegroundWindow());
    }

    /// <summary> Capture a .NET Control, Form, UserControl, etc. </summary>
    /// <param name="c">Object to capture</param>
    /// <returns> Bitmap of control's area </returns>
    public static Bitmap Capture(Control c)
    {
        return Capture(c.Handle);
    }

    /// <summary> Capture a specific window and return it as a bitmap </summary>
    /// <param name="handle">hWnd (handle) of the window to capture</param>
    public static Bitmap Capture(IntPtr handle)
    {
        var rect = new Rect();
        GetWindowRect(handle, ref rect);
        var bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
        CursorPosition = new Point(Cursor.Position.X - rect.Left, Cursor.Position.Y - rect.Top);

        var result = new Bitmap(bounds.Width, bounds.Height);
        using (var g = Graphics.FromImage(result))
        {
            g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
        }

        return result;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct Rect
    {
        public readonly int Left;
        public readonly int Top;
        public readonly int Right;
        public readonly int Bottom;
    }
Timon Post
  • 2,779
  • 1
  • 17
  • 32
  • Any reason why you are mixing WinForms and WPF bitmap APIs for conversion to and from byte arrays? How does `System.Drawing.ImageConverter` actually encode a bitmap frame? You may probably better use an explicit conversion, e.g. a WPF PngBitmapEncoder. – Clemens May 11 '17 at 18:11
  • 1)It is a socket program so the server sends a request an image and client send a response in byte[]. Clients do not need a user friendly UI that is why I made a console application. 2) Why I cant use PngBitmapEncoder or other methods, because they need some location where an image is stored, like PngBitmapEncoder in a FileStream. I don't want to safe it first. 3)I used your example http://stackoverflow.com/questions/30727343/fast-converting-bitmap-to-bitmapsource-wpf but it will say 'Wallpaper.Source' threw an exception of type 'System.InvalidOperationException' when setting to Image element – Timon Post May 11 '17 at 19:07
  • WPF BitmapEncoders can save to any stream, not only FileStream. You would save to a MemoryStream, then get the byte array from its `ToArray` method. – Clemens May 11 '17 at 19:26

0 Answers0