iOS 屏幕截图为来自后台应用程序的 System.Drawing.Bitmap (Xamarin)
Posted
技术标签:
【中文标题】iOS 屏幕截图为来自后台应用程序的 System.Drawing.Bitmap (Xamarin)【英文标题】:iOS Screenshot as System.Drawing.Bitmap (Xamarin) from Background App 【发布时间】:2017-02-27 05:42:53 【问题描述】:对于越狱的 iOS 设备上的私人应用(=不发布在任何应用商店),我需要截取整个屏幕的屏幕截图,以免发送到背景。
已发布类似问题
ios: Is it possible to take screenshots while running as a background task? 得出的结论是,由于 iOS 的安全沙箱,在非越狱设备上是不可能的 How to take screenshots of running iPhone from background app? 尚待处理,但似乎也针对非越狱设备 How to take screenshot of entire screen on a Jailbroken iOS Device?(和 jailbroken iOS taking screenshot from background app 有点相似)似乎与我要找的很接近,但代码是用 Objective C 编写的,并使用了一个神秘的私有函数 _UICreateScreenUIImage()我不知道它是在哪个模块中定义的(所以我不知道如何将它声明为外部函数)。屏幕截图也作为 UIImage(参见 http://developer.xamarin.com/api/type/MonoTouch.UIKit.UIImage/)返回,我还不知道如何转换为 System.Drawing.Bitmap(参见 https://developer.xamarin.com/api/type/System.Drawing.Bitmap/)。以下是我的代码的帖子。我正在寻求有关private System.Drawing.Bitmap ShootScreen()
实施的帮助
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
namespace ***Snippets
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://***.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:截图需要是System.Drawing.Bitmap
,因为之后我需要对其进行Pixelwise操作
编辑 1
经过进一步调查,我在http://sharpmobilecode.com/introduction-to-xamarin-what-it-is-what-it-isnt/ 发现以下段落
例如,假设您现有的 C# 代码使用 System.Drawing.Bitmap。这是您编写的多个通用库 桌面应用参考。一种可能的方法如下所示:
using System.Drawing; public Bitmap GetBitmapFromCache(string fileName) return (Bitmap) Image.FromFile(fileName);
如果要在 Xamarin.iOS 或 Xamarin.android 应用程序中编译此方法,则会收到编译错误。嗯?之上 进一步调查,您会看到
System.Drawing.Bitmap
和System.Drawing.Image
未定义。什么!?!?这是有效的 C# 句法!经过进一步调查,您只看到几个类System.Drawing
实际上已定义(RectangleF、PointF 等)。在哪里 到底是位图和图像?嗯,这些是有充分理由的 Mono 框架中没有实现类。这是因为他们 不兼容跨平台。这到底是什么意思?让我们看一下 System.Drawing MSDN 上的文档。它指出,“System.Drawing 命名空间 提供对 GDI+ 基本图形功能的访问。”。什么是 GDI+? GDI+(图形设备接口)是一个图形库 (Win32),它是 特定于 Windows 操作系统。所以在一个引擎盖下 System.Drawing.Bitmap,是一个 Win32 实现,与 Windows 桌面/服务器操作系统。这意味着 System.Drawing.Bitmap 和 System.Drawing.Image 是平台相关的, 并且仅适用于 Windows 桌面/服务器应用程序。你将无法 在 iOS 应用、Android 应用甚至 Windows Phone 上使用这些类 应用。那么如何在 iOS 或 Android 中使用当前的 C# 代码 应用程序?稍作修改如下:
public byte[] GetBitmapFromCache(string fileName) return File.ReadAllBytes(fileName);
iOS 和 Android 没有 GDI+ 位图的概念 (System.Drawing.Bitmap),但是,每个平台都知道如何转换 一个字节数组自己实现的位图。使用 iOS,您可以从字节数组创建 UIImage,而在 Android 您可以从同一个数组创建一个 Android.Graphics.Bitmap。
【问题讨论】:
【参考方案1】:如http://sharpmobilecode.com/introduction-to-xamarin-what-it-is-what-it-isnt/ 中所述,无法创建System.Drawing.Bitmap
,因为System.Drawing
命名空间提供了对Windows 操作系统特有的GDI+ 基础图形功能的访问,因此在iOS 上根本不可用。
但是,可以将屏幕截图创建为UIImage
,如下所示
How to take screenshot of entire screen on a Jailbroken iOS Device? 表明,存在一个私有函数 _UICreateScreenUIImage(void)
,它返回所需的屏幕截图。为了访问这个函数,必须声明它
using System.Runtime.InteropServices;
using ObjCRuntime;
[DLLImport(/*ObjCRuntime.*/Constants.UIKitLibrary)]
extern static IntPtr _UICreateScreenUIImage();
调用时,返回的IntPtr指向一个原生的UIImage,需要包装成一个托管对象,由Runtime.GetNSObject(IntPtr)
完成
IntPtr ptr = _UICreateScreenUIImage();
UIImage uiimg = Runtime.GetNSObject(ptr) as UIImage;
生成的uiimg
包含屏幕截图
所附图片包含一个关于如何创建屏幕截图并将其保存为.jpeg
的简约示例
还有一个应用程序在后台时屏幕截图的示例
【讨论】:
以上是关于iOS 屏幕截图为来自后台应用程序的 System.Drawing.Bitmap (Xamarin)的主要内容,如果未能解决你的问题,请参考以下文章