0

For a private app (=not to be published on any appstore) on a jailbroken iOS-Device I need to take screenshots of the entire screen from being sent to the background.

Similar questions have been posted

The following is a post of my code. I am looking for help with the implementation of private System.Drawing.Bitmap ShootScreen()

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;

namespace StackOverflowSnippets
{
    public sealed class ScreenshooterThread
    {
        private Thread _t = null;                   // Thread-Object: Execution-Helper
        private Boolean _terminate = false;         // Stop current execution?
        private Int32 _intervalMS = 0;              // Take Screenshots every ? MS
        private Queue<Bitmap> _q;                   // Queue to store the Screenshots

        #region Interface
        /// <summary>
        /// Creates a Screenshooter
        /// </summary>
        /// <param name="intervalMS">Defines every how many MS a screenshot should be taken</param>
        public ScreenshooterThread(Int32 intervalMS = 200)
        {
            _intervalMS = intervalMS;
        }

        /// <summary>
        /// Starts taking Screenshots
        /// </summary>
        public void Run()
        {
            if(this.IsRunning || this.IsTerminating)
            {
                throw new InvalidOperationException("ScreenshooterThread is currently running or terminating");
            }

            lock (this)
            {
                _terminate = false;
                _t = new Thread(this.DoWork);
                _t.Start();
            }
        }

        /// <summary>
        /// Stops taking Screenshots
        /// </summary>
        public void Stop()
        {
            if (!this.IsRunning) return; if (this.IsTerminating) return;

            lock(this)
            {
                _terminate = true;
            }
        }

        /// <summary>
        /// Determines whether the Thread is currently running
        /// </summary>
        public Boolean IsRunning
        {
            get
            {
                if (_t == null) return false;

                return _t.IsAlive;
            }
        }
        /// <summary>
        /// Determines whether Thread-Termination has been invoked
        /// </summary>
        public Boolean IsTerminating
        {
            get
            {
                return _terminate;
            }
        }

        /// <summary>
        /// Get a Screenshot from the Queue. Returns null if none available
        /// </summary>
        public Bitmap Deqeue()
        {
            lock(_q)
            {
                if (_q.Count < 1) return null;
                return _q.Dequeue();
            }
        }

        #endregion

        #region Implementation Details
        /// <summary>
        /// Add a Screenshot to the Queue
        /// </summary>
        private void Enqueue(Bitmap b)
        {
            lock(_q)
            {
                _q.Enqueue(b);
            }
        }

        /// <summary>
        /// Task-Implementation while running
        /// </summary>
        private void DoWork()
        {
            while(!_terminate)
            {
                if (_intervalMS > 0) Thread.Sleep(_intervalMS);

                this.Enqueue(this.ShootScreen());
            }

            this.CleanUp();
        }

        /// <summary>
        /// Takes a Screenshot of the device even if the app is in the Background
        /// </summary>
        private System.Drawing.Bitmap ShootScreen()
        {
            // System.Drawing.Bitmap supported by Xamarin
            // https://developer.xamarin.com/api/type/System.Drawing.Bitmap/

            // Method in ObjC: https://stackoverflow.com/questions/33369014/how-to-take-screenshot-of-entire-screen-on-a-jailbroken-ios-device

            throw new NotImplementedException();
        }
        /// <summary>
        /// Preparing for next launch
        /// </summary>
        private void CleanUp()
        {
            lock(this)
            {
                _terminate = false;
                _t = null;
            }
        }
        #endregion
    }
}

PS: The screenshots need to be a System.Drawing.Bitmap because I need to perform Pixelwise operations on it afterwards

EDIT 1

Upon further investigation I found on http://sharpmobilecode.com/introduction-to-xamarin-what-it-is-what-it-isnt/ the following paragraph

For example, let’s say you have existing C# code that uses System.Drawing.Bitmap. It’s a common library you wrote that multiple Desktop apps reference. A possible method looks something like this:

using System.Drawing; 

public Bitmap GetBitmapFromCache(string fileName) 
{ 
   return (Bitmap) Image.FromFile(fileName); 
}    

If you were to compile this method in a Xamarin.iOS or Xamarin.Android app,you would get a compile error. Huh? Upon further investigation, you would see that System.Drawing.Bitmap and System.Drawing.Image are not define. What!?!? That’s valid C# syntax! Upon further investigation, you only see a few classes in System.Drawing are actually defined (RectangleF, PointF, etc). Where the heck is Bitmap and Image? Well, there is a good reason these classes are not implemented in the Mono Framework. It’s because they are not cross platform compatible.

What does that mean exactly? Let’s take a look at the System.Drawing documentation on MSDN. It states, “The System.Drawing namespace provides access to GDI+ basic graphics functionality.”. What is GDI+? GDI+ (Graphics Device Interface) is a graphics library (Win32) that is specific to the Windows Operating System. So underneath the hood of a System.Drawing.Bitmap, is a Win32 implementation that ties into the Windows Desktop/Server Operating systems. That means System.Drawing.Bitmap and System.Drawing.Image are platform dependent, and only work on Windows Desktop/Server apps. You will not be able to use these classes on iOS app, Android apps, or even Windows Phone apps. So how can you use this current C# code in an iOS or Android app? With a slight modification like this:

public byte[] GetBitmapFromCache(string fileName) 
{ 
    return File.ReadAllBytes(fileName); 
} 

iOS and Android have no concept of GDI+ bitmap (System.Drawing.Bitmap), however, each platform knows how to convert an array of bytes into their own implementation of a bitmap. Using iOS, you can create a UIImage from an array of bytes, and in Android you can create an Android.Graphics.Bitmap from that same array.

Community
  • 1
  • 1
Benj
  • 889
  • 1
  • 14
  • 31

1 Answers1

0

As stated under http://sharpmobilecode.com/introduction-to-xamarin-what-it-is-what-it-isnt/ it is not possible to create a System.Drawing.Bitmap because the System.Drawing namespace provides access to GDI+ basics graphics functionality which is specific to the Windows OS and therefore simply not available on iOS.

It is however possible to create a screenshot as an UIImage as follows

  1. How to take screenshot of entire screen on a Jailbroken iOS Device? showed, that there exists a private function _UICreateScreenUIImage(void) that returns the desired screenshot. In order to access this function, it must be declared

    using System.Runtime.InteropServices;
    using ObjCRuntime;
    
    [DLLImport(/*ObjCRuntime.*/Constants.UIKitLibrary)]
    extern static IntPtr _UICreateScreenUIImage();
    
  2. When called, the returned IntPtr points to a native UIImage and needs to be wrapped into a managed object, which is done by Runtime.GetNSObject(IntPtr)

    IntPtr ptr = _UICreateScreenUIImage();
    UIImage uiimg = Runtime.GetNSObject(ptr) as UIImage;
    

    The resulting uiimg contains the screenshot

The attached picture contains a minimalistic example on how to create a screenshot and save it as a .jpeg

Minimalistic example on how to create and save a screenshot

And an example of what a screenshot might look like when the app is in the background

Picture of what a screenshot looks like

Community
  • 1
  • 1
Benj
  • 889
  • 1
  • 14
  • 31