Is there a method of screenshot for all virtual display? - c#

All the methods I've found are somehow UI dependent. So, how to capture all displays without any dependency and third party?

You can use GetSystemMetrics function with SM_CXVIRTUALSCREEN and SM_CYVIRTUALSCREEN parameters for entire desktop. It will be handle all virtual displays.
using System;
using System.ComponentModel;//You can remove it if you will catch the below exception from another layer
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
namespace Helpers {
public static class HelperScreenShot {
public static void TakeAndSaveByAllScreen(ImageFormat imageFormat, string fullFileName = null) {
var img = CopyFromScreen();
if(img == null) return;
if(string.IsNullOrWhiteSpace(fullFileName)) {
fullFileName = $"{Guid.NewGuid()}.{imageFormat.ToString().ToLower()}";
}
img.Save(fullFileName, imageFormat);
}
public static byte[] TakeAsBinary(ImageFormat imageFormat) {
using var image = CopyFromScreen();
if(image == null) return default;
using var ms = new MemoryStream();
image.Save(ms, imageFormat);
return ms.ToArray();
}
private static Bitmap CopyFromScreen() {
try {
var size = HelperDisplay.GetVirtualScreenSize();
var image = new Bitmap(size.Width, size.Height);
using var graphics = Graphics.FromImage(image);
graphics.CopyFromScreen(Point.Empty, Point.Empty, size);
return image;
}
catch(Win32Exception) {//When screen saver is active
return null;
}
}
private static class HelperDisplay {
[DllImport("user32.dll")]
private static extern int GetSystemMetrics(MetricType metricType);
public static Size GetVirtualScreenSize() {
var width = GetSystemMetrics(MetricType.VirtualScreenWidth);
var height = GetSystemMetrics(MetricType.VirtualScreenHeight);
return new Size(width, height);
}
private enum MetricType {
VirtualScreenWidth = 78,
VirtualScreenHeight = 79
}
}
}
}
Usage
var dir = Path.Combine("images", "screenshot");
var imgFormat = ImageFormat.Png;
var fileName = $"{Guid.NewGuid()}.{imgFormat.ToString().ToLower()}";
fileName = Path.Combine(dir, fileName);
if(!Directory.Exists(dir)) {
Directory.CreateDirectory(dir);
}
HelperScreenShot.TakeAndSaveByAllScreen(fileName, imgFormat);
//or
var binaryImg = HelperScreenShot.TakeAsBinary(imgFormat);
Further Reading:
Multiple Monitor System Metrics
winuser.h header
How do you use System.Drawing in .NET Core?

Related

How to get drawable path?

public async Task<List<AppItem>> GetAndroidApps()
{
try
{
var apps = Android.App.Application.Context.PackageManager.GetInstalledApplications(PackageInfoFlags.MatchAll);
foreach(var item in apps)
{
string packageName = item.PackageName;
string appName = item.LoadLabel(Android.App.Application.Context.PackageManager);
var appIcon = item.LoadIcon(Android.App.Application.Context.PackageManager).ToString();
System.Diagnostics.Debug.WriteLine($"{appIcon}; {appName}; {packageName};");
}
return null;
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
return null;
}
}
How I can get appIcon path to display it?
I can't get full path to other app icon...
Thank you!
Best wishes!
You could load the icon from the drawable resource identifier (in the package's resources) of this component's. And set the imagesource for imageview.
I use my App6 for example to load the icon. It works.
var apps = Android.App.Application.Context.PackageManager.GetInstalledApplications(PackageInfoFlags.MatchAll);
foreach (var item in apps)
{
string packageName = item.PackageName;
string appName = item.LoadLabel(Android.App.Application.Context.PackageManager);
if (packageName == "com.companyname.app6")
{
var appIcon = item.LoadIcon(Android.App.Application.Context.PackageManager).ToString();
var image = item.Icon;
imageView.SetImageResource(image);
}
//System.Diagnostics.Debug.WriteLine($"{appIcon}; {appName}; {packageName};");
}
Updated:
You could use the DependentService to do that.
I convet the resID of deawable to bitmap and then convert the bitmap to stream. At last, i set the image source form the stream in Xamarin.forms.
Create the interface in Xamarin.forms.
public interface GetIcon
{
byte[] GetIconFromApp();
}
Implementation in Android:
[assembly: Dependency(typeof(GetIconFromApp_Android))]
namespace App13.Droid
{
class GetIconFromApp_Android : GetIcon
{
public byte[] GetIconFromApp()
{
var apps = Android.App.Application.Context.PackageManager.GetInstalledApplications(PackageInfoFlags.MatchAll);
byte[] bitmapData=null;
foreach (var item in apps)
{
string packageName = item.PackageName;
string appName = item.LoadLabel(Android.App.Application.Context.PackageManager);
if (packageName == "com.companyname.app6")
{
var appIcon = item.LoadIcon(Android.App.Application.Context.PackageManager).ToString();
var imageID = item.Icon;
// Converting Drawable Resource to Bitmap
var bitmap = BitmapFactory.DecodeResource(Forms.Context.Resources, imageID);
//comver bitmap to byte
using (var stream = new MemoryStream())
{
bitmap.Compress(Android.Graphics.Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
}
}
return bitmapData;
}
}
}
Platform implementations with button click event.
private void Button_Clicked(object sender, EventArgs e)
{
var ImageByteArray = DependencyService.Get<GetIcon>().GetIconFromApp();
image.Source = ImageSource.FromStream(() => new MemoryStream(ImageByteArray, 0, ImageByteArray.Length));
}
Screenshot:

Exception when creating new XFont. PDFSharp Xamarin

When i debugging my app I got Exception when creating object of XFont. Any idea what is wrong?
In all examples I found on github etc. it was exactly like in this code below.
System.TypeInitializationException: 'The type initializer for
'PdfSharpCore.Utils.FontResolver' threw an exception.'
document.Info.Title = "created with pdfsharp";
// Create an empty page
PdfPage page = document.AddPage();
// Get an XGraphics object for drawing
XGraphics gfx = XGraphics.FromPdfPage(page);
// Create a font
XFont font = new XFont("Verdana", 20, XFontStyle.BoldItalic);
// Draw the text
gfx.DrawString("Test of PdfSharp on Android", font,
new XSolidBrush(XColor.FromArgb(0, 0, 0)),
10, 130);
// Save the document...
document.Save(Path.Combine(Path.GetTempPath(), Name));
According to your code, I guess that you use PdfSharp.Xamarin.Forms library, now you want to use custom fonts for pdf, am I right?
if yes, please take a look the following code, implementing IFontResolver.
internal class FontProvider : IFontResolver
{
#region Properties
public string DefaultFontName
{
get { return "OpenSans"; }
}
#endregion
#region Fields
public ICustomFontProvider _fontProvider;
public static readonly string[] DefaultFontFiles = new string[]
{
"OpenSans-Regular.ttf",
"OpenSans-Bold.ttf",
"OpenSans-Italic.ttf",
"OpenSans-BoldItalic.ttf",
};
#endregion
#region Ctor
public FontProvider(ICustomFontProvider fontProvider)
{
_fontProvider = fontProvider;
}
#endregion
#region IFontResolver implementation
public byte[] GetFont(string faceName)
{
if (DefaultFontFiles.Contains(faceName) || _fontProvider == null)
{
var assembly = typeof(FontProvider).GetTypeInfo().Assembly;
Stream stream = assembly.GetManifestResourceStream($"PdfSharp.Xamarin.Forms.DefaultFonts.{faceName}");
using (var reader = new StreamReader(stream))
{
var bytes = default(byte[]);
using (var memstream = new MemoryStream())
{
reader.BaseStream.CopyTo(memstream);
bytes = memstream.ToArray();
}
return bytes;
}
}
else
{
return _fontProvider.GetFont(faceName);
}
}
public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic)
{
string fontName;
if (familyName == DefaultFontName || _fontProvider == null)
fontName = DefaultFontFiles[Convert.ToInt32(isBold) + 2 * Convert.ToInt32(isItalic)];
else
fontName = _fontProvider.ProvideFont(familyName, isBold, isItalic);
return new FontResolverInfo(fontName);
}
#endregion
}
You can also write my own implementation of IFontResolver and assigning it to GlobalFontSettings.FontResolver.
Please take a look the following thread:
Loading a Font with PdfSharp .Net Standard preview from Xamarin.Forms fails: No appropriate font found

How to check "Is Valid Image File" in C# [duplicate]

This question already has answers here:
Validate image from file in C#
(14 answers)
Closed 5 years ago.
I'm trying not using try-catch to achieve this goal
here's my code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Bitmap imagePattern = new Bitmap(#"test.jpg");
Console.Read();
}
}
}
But if test.jpg is damage then C# will show error, so my question: is there a function like IsValidImage() in C#?
THX!
I think you can check the file header.
public static ImageType CheckImageType(string path)
{
byte[] buf = new byte[2];
try
{
using (StreamReader sr = new StreamReader(path))
{
int i = sr.BaseStream.Read(buf, 0, buf.Length);
if (i != buf.Length)
{
return ImageType.None;
}
}
}
catch (Exception exc)
{
//Debug.Print(exc.ToString());
return ImageType.None;
}
return CheckImageType(buf);
}
public static ImageType CheckImageType(byte[] buf)
{
if (buf == null || buf.Length < 2)
{
return ImageType.None;
}
int key = (buf[1] << 8) + buf[0];
ImageType s;
if (_imageTag.TryGetValue(key, out s))
{
return s;
}
return ImageType.None;
}
public enum ImageType
{
None = 0,
BMP = 0x4D42,
JPG = 0xD8FF,
GIF = 0x4947,
PCX = 0x050A,
PNG = 0x5089,
PSD = 0x4238,
RAS = 0xA659,
SGI = 0xDA01,
TIFF = 0x4949
}

Extract powerpoint titles with C#

I have powerponint 97-2003 files(.ppt extension) and I need to extract slide titles programatically using C#.
I have tried using Microsoft.Office.Interop but without success.
I have search with google and as a maximum I have found how to obtain reference to PowerPoint.Slide:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Core;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
namespace Tester
{
class Program
{
static void Main(string[] args)
{
Microsoft.Office.Interop.PowerPoint.Application presentationApp = new Microsoft.Office.Interop.PowerPoint.Application();
try
{
string pptPath = #"D:\somefile.ppt";
TestReadingTitles(presentationApp, pptPath);
}
finally
{
presentationApp.Quit();
}
}
private static void TestReadingTitles(Microsoft.Office.Interop.PowerPoint.Application presentationApp, string pptPath)
{
presentationApp.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
Microsoft.Office.Interop.PowerPoint.Presentations presentations = presentationApp.Presentations;
Microsoft.Office.Core.MsoTriState readOnly = Microsoft.Office.Core.MsoTriState.msoTrue;
Microsoft.Office.Core.MsoTriState untitled = Microsoft.Office.Core.MsoTriState.msoTrue;
Microsoft.Office.Core.MsoTriState withWindow = Microsoft.Office.Core.MsoTriState.msoFalse;
Microsoft.Office.Interop.PowerPoint.Presentation presentation = presentations.Open(pptPath, readOnly, untitled, withWindow);
for (int i = 0; i < presentation.Slides.Count; i++)
{
foreach (PowerPoint.Slide slide in presentation.Slides)
{
string slidetitle = ??????????????????;
}
}
}
}
}
You can extract the titles without looping through the shapes.
private static string ExtractSlideTitlefromSlide(Microsoft.Office.Interop.PowerPoint.Slide slide, string defaultValue)
{
if (slide.Shapes.HasTitle == Office.MsoTriState.msoTrue)
{
if (slide.Shapes.Title.TextFrame.HasText == Office.MsoTriState.msoTrue)
{
return slide.Shapes.Title.TextFrame.TextRange.Text;
}
}
return defaultValue;
}
I have no solution for direct extract slide titles from ppt. This is a workarround - first temproaly convert it into pptx and then extract titles using openxml.
For conversion from ppt to pptx I have used Microsoft Interop which I do not like but I have no better solution.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;
using D = DocumentFormat.OpenXml.Drawing;
using Shape = DocumentFormat.OpenXml.Presentation.Shape;
namespace Tester
{
class Program
{
static void Main(string[] args)
{
string pptPath = #"D:\mypresentation.ppt";
ReadTitles(pptPath);
}
private static void ReadTitles(string pptPath)
{
IList<string> slideTitles = GetSlidesTitles(pptPath);
Debug.Print("SLIDES TITLES FOR {0}:", pptPath);
foreach (string slideTitle in slideTitles)
{
Debug.Print("\t {0}", slideTitle);
}
}
private static IList<string> GetSlidesTitles(string pptPath)
{
string pptxPath = SaveAsPptx(pptPath);
IList<string> titles = GetSlideTitles(pptxPath);
try
{
File.Delete(pptxPath);
Debug.Print("Temporary pptx file {0} deleted.", pptxPath);
}
catch (Exception e)
{
Debug.Print("Error deleting file {0}. ERROR: {1}", pptxPath, e.Message);
}
return titles;
}
private static string SaveAsPptx(string pptPathIn)
{
Microsoft.Office.Interop.PowerPoint.Application presentationApp = new Microsoft.Office.Interop.PowerPoint.Application();
string pptxPathOut = null;
try
{
string pptDir = Path.GetDirectoryName(pptPathIn);
string pptFileNameOnly = Path.GetFileNameWithoutExtension(pptPathIn);
pptxPathOut = Path.Combine(pptDir, pptFileNameOnly + ".pptx");
presentationApp.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
Microsoft.Office.Interop.PowerPoint.Presentations presentations = presentationApp.Presentations;
Microsoft.Office.Core.MsoTriState readOnly = Microsoft.Office.Core.MsoTriState.msoFalse;
Microsoft.Office.Core.MsoTriState untitled = Microsoft.Office.Core.MsoTriState.msoFalse;
Microsoft.Office.Core.MsoTriState withWindow = Microsoft.Office.Core.MsoTriState.msoFalse;
Debug.Print("Opening ppt file {0} ...", pptPathIn);
Microsoft.Office.Interop.PowerPoint.Presentation presentation = presentations.Open(pptPathIn, readOnly, untitled, withWindow);
Debug.Print("Starting creation of pptx from ppt {0}", pptPathIn);
presentation.SaveCopyAs(pptxPathOut, PowerPoint.PpSaveAsFileType.ppSaveAsOpenXMLPresentation, Microsoft.Office.Core.MsoTriState.msoFalse);
Debug.Print("Successfully created pptx {0} from ppt {1}", pptxPathOut, pptPathIn);
}
catch (Exception e)
{
Debug.Print("Error during creating pptx from ppt " + pptPathIn, e);
}
finally
{
presentationApp.Quit();
}
return pptxPathOut;
}
// Get a list of the titles of all the slides in the presentation.
public static IList<string> GetSlideTitles(string presentationFile)
{
// Open the presentation as read-only.
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, false))
{
return GetSlideTitles(presentationDocument);
}
}
// Get a list of the titles of all the slides in the presentation.
public static IList<string> GetSlideTitles(PresentationDocument presentationDocument)
{
if (presentationDocument == null)
{
throw new ArgumentNullException("presentationDocument");
}
// Get a PresentationPart object from the PresentationDocument object.
PresentationPart presentationPart = presentationDocument.PresentationPart;
if (presentationPart != null &&
presentationPart.Presentation != null)
{
// Get a Presentation object from the PresentationPart object.
Presentation presentation = presentationPart.Presentation;
if (presentation.SlideIdList != null)
{
List<string> titlesList = new List<string>();
// Get the title of each slide in the slide order.
foreach (var slideId in presentation.SlideIdList.Elements<SlideId>())
{
SlidePart slidePart = presentationPart.GetPartById(slideId.RelationshipId) as SlidePart;
// Get the slide title.
string title = GetSlideTitle(slidePart);
// An empty title can also be added.
titlesList.Add(title);
}
return titlesList;
}
}
return null;
}
// Get the title string of the slide.
public static string GetSlideTitle(SlidePart slidePart)
{
if (slidePart == null)
{
throw new ArgumentNullException("slidePart");
}
// Declare a paragraph separator.
string paragraphSeparator = null;
if (slidePart.Slide != null)
{
// Find all the title shapes.
var shapes = from shape in slidePart.Slide.Descendants<Shape>()
where IsTitleShape(shape)
select shape;
StringBuilder paragraphText = new StringBuilder();
foreach (var shape in shapes)
{
// Get the text in each paragraph in this shape.
foreach (var paragraph in shape.TextBody.Descendants<D.Paragraph>())
{
// Add a line break.
paragraphText.Append(paragraphSeparator);
foreach (var text in paragraph.Descendants<D.Text>())
{
paragraphText.Append(text.Text);
}
paragraphSeparator = "\n";
}
}
return paragraphText.ToString();
}
return string.Empty;
}
// Determines whether the shape is a title shape.
private static bool IsTitleShape(Shape shape)
{
var placeholderShape = shape.NonVisualShapeProperties.ApplicationNonVisualDrawingProperties.GetFirstChild<PlaceholderShape>();
if (placeholderShape != null && placeholderShape.Type != null && placeholderShape.Type.HasValue)
{
switch ((PlaceholderValues)placeholderShape.Type)
{
// Any title shape.
case PlaceholderValues.Title:
// A centered title.
case PlaceholderValues.CenteredTitle:
return true;
default:
return false;
}
}
return false;
}
}
}
Finally I have found a way to get out powerpoint presentation titles from .ppt file without converting it to .pptx. Here is a solution:
using System;
using System.Collections.Generic;
using Microsoft.Office.Core;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
namespace Mintra.Publisher.DocumentConverter.Core.Utils
{
class InteropUtility
{
public static IList<string> GetPresentationTitles(string pptPath)
{
IList<string> result = new List<string>();
var presentationApp = new Microsoft.Office.Interop.PowerPoint.Application();
try
{
presentationApp.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
Microsoft.Office.Interop.PowerPoint.Presentations presentations = presentationApp.Presentations;
var readOnly = Microsoft.Office.Core.MsoTriState.msoTrue;
var untitled = Microsoft.Office.Core.MsoTriState.msoTrue;
var withWindow = Microsoft.Office.Core.MsoTriState.msoFalse;
Microsoft.Office.Interop.PowerPoint.Presentation presentation = presentations.Open(pptPath, readOnly, untitled, withWindow);
int i = 0;
foreach (PowerPoint.Slide slide in presentation.Slides)
{
string defaultTitle = String.Format("Slide {0}", i);
String shapeTitle = ExtractSlideTitlefromShape(slide, defaultTitle);
result.Add(shapeTitle);
}
}
finally
{
presentationApp.Quit();
}
return result;
}
private static string ExtractSlideTitlefromShape(PowerPoint.Slide slide, string defaultValue)
{
PowerPoint.HeadersFooters headersFooters = slide.HeadersFooters;
PowerPoint.Shapes mastershapes = slide.Master.Shapes;
for (int i = 1; i <= slide.Shapes.Count; i++)
{
PowerPoint.Shape shape = slide.Shapes[i];
bool hasTextFrame = shape.HasTextFrame == MsoTriState.msoTrue;
bool isTypePlaceholder = shape.Type.Equals(MsoShapeType.msoPlaceholder);
bool hasTextInTextFrame = shape.TextFrame.HasText == MsoTriState.msoTrue;
bool isTitleShape = shape.Name.ToLower().Contains("title");
if (isTypePlaceholder && hasTextFrame && hasTextInTextFrame && isTitleShape)
{
return shape.TextFrame.TextRange.Text;
}
}
return defaultValue;
}
}
}
Microsoft.Office.Interop.PowerPoint.Application pptApplication = new Microsoft.Office.Interop.PowerPoint.Application();
Microsoft.Office.Interop.PowerPoint.Slides slides;
Microsoft.Office.Interop.PowerPoint._Slide slide;
// Create the Presentation File
Presentation pptPresentation = pptApplication.Presentations.Add(MsoTriState.msoTrue);
for (int i = 0; i < 2; i++)
{
Microsoft.Office.Interop.PowerPoint.CustomLayout customLayout = pptPresentation.SlideMaster.CustomLayouts[Microsoft.Office.Interop.PowerPoint.PpSlideLayout.ppLayoutChartAndText];
// customLayout.t
// Create new Slide
slides = pptPresentation.Slides;
slide = slides.AddSlide(1, customLayout);
slide.Shapes.Title.Top = 0;
slide.Shapes.Title.TextFrame.TextRange.Text = "Welcome!";
All you need is change Welcome text.

Dropped zip file causes e.Data.GetData("FileContents") to throw an exception

I'm trying to implement a handler in my WPF application for files dragged from a zip archive. The handler should get the file content for further processing.
My environment: Windows7, 7-zip installed, Visual Studio 2012 Express, .Net 4.5
Here is the code of a simple MainWindow app to demonstrate the problem:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AllowDrop= true;
Drop += onDrop;
}
private void onDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent("FileContents"))
{
var fileContents = e.Data.GetData("FileContents");
//get file contents...
}
}
}
When I drag a file contained in a zip archive to my Window the call to e.Data.GetData("FileContents") throws a System.ArgumentException ("Argument out of Range") with the following callstack:
System.Windows.DataObject.OleConverter.GetDataInner(formatetc, medium)
System.Windows.DataObject.OleConverter.GetDataFromOleHGLOBAL(format, aspect, index)
System.Windows.DataObject.OleConverter.GetDataFromBoundOleDataObject(format, aspect, index)
System.Windows.DataObject.OleConverter.GetData(format, autoConvert, aspect, index)
System.Windows.DataObject.OleConverter.GetData(format, autoConvert)
System.Windows.DataObject.GetData(format, autoConvert)
System.Windows.DataObject.GetData(format)
TestZip.MainWindow.onDrop(sender, e) Zeile 34 C#
I've looked up the source code of this OleConverter (http://reflector.webtropy.com/default.aspx/Dotnetfx_Win7_3#5#1/Dotnetfx_Win7_3#5#1/3#5#1/DEVDIV/depot/DevDiv/releases/Orcas/NetFXw7/wpf/src/Core/CSharp/System/Windows/dataobject#cs/1/dataobject#cs) but the GetDataInner() method is implemented like
private void GetDataInner(ref FORMATETC formatetc, out STGMEDIUM medium)
{
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // BlessedAssert
try
{
_innerData.GetData(ref formatetc, out medium);
}
finally
{
SecurityPermission.RevertAssert();
}
}
So this does also not provide further info of what's wrong here.
I also tried with uninstalled 7-zip and with different zip archives, but no change.
My question: Does any one have a clue what's going wrong here? What do I need to do in order to get the content of a file from a zip-archive dropped onto my window?
Old question but I needed to do this today so....
Using statements:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
Reference PresentationCore
https://msdn.microsoft.com/en-us/library/system.windows.idataobject(v=vs.110).aspx
First you need to get the "FileDescriptor" contents, here's a class which reads them.
/// <summary>
/// Specifies which fields are valid in a FileDescriptor Structure
/// </summary>
[Flags]
enum FileDescriptorFlags : uint
{
ClsId = 0x00000001,
SizePoint = 0x00000002,
Attributes = 0x00000004,
CreateTime = 0x00000008,
AccessTime = 0x00000010,
WritesTime = 0x00000020,
FileSize = 0x00000040,
ProgressUI = 0x00004000,
LinkUI = 0x00008000,
Unicode = 0x80000000,
}
internal static class FileDescriptorReader
{
internal sealed class FileDescriptor
{
public FileDescriptorFlags Flags{get;set;}
public Guid ClassId{get;set;}
public Size Size{get;set;}
public Point Point{get;set;}
public FileAttributes FileAttributes{get;set;}
public DateTime CreationTime{get;set;}
public DateTime LastAccessTime{get;set;}
public DateTime LastWriteTime{get;set;}
public Int64 FileSize{get;set;}
public string FileName{get;set;}
public FileDescriptor(BinaryReader reader)
{
//Flags
Flags = (FileDescriptorFlags)reader.ReadUInt32();
//ClassID
ClassId = new Guid(reader.ReadBytes(16));
//Size
Size = new Size(reader.ReadInt32(), reader.ReadInt32());
//Point
Point = new Point(reader.ReadInt32(), reader.ReadInt32());
//FileAttributes
FileAttributes = (FileAttributes)reader.ReadUInt32();
//CreationTime
CreationTime = new DateTime(1601,1,1).AddTicks(reader.ReadInt64());
//LastAccessTime
LastAccessTime = new DateTime(1601,1,1).AddTicks(reader.ReadInt64());
//LastWriteTime
LastWriteTime = new DateTime(1601, 1, 1).AddTicks(reader.ReadInt64());
//FileSize
FileSize = reader.ReadInt64();
//FileName
byte[] nameBytes = reader.ReadBytes(520);
int i = 0;
while(i < nameBytes.Length)
{
if (nameBytes[i] == 0 && nameBytes[i + 1] == 0)
break;
i++;
i++;
}
FileName = UnicodeEncoding.Unicode.GetString(nameBytes, 0, i);
}
}
public static IEnumerable<FileDescriptor> Read(Stream fileDescriptorStream)
{
BinaryReader reader = new BinaryReader(fileDescriptorStream);
var count = reader.ReadUInt32();
while (count > 0)
{
FileDescriptor descriptor = new FileDescriptor(reader);
yield return descriptor;
count--;
}
}
public static IEnumerable<string> ReadFileNames(Stream fileDescriptorStream)
{
BinaryReader reader = new BinaryReader(fileDescriptorStream);
var count = reader.ReadUInt32();
while(count > 0)
{
FileDescriptor descriptor = new FileDescriptor(reader);
yield return descriptor.FileName;
count--;
}
}
}
Now using that you can get the matching file content for each file:
static class ClipboardHelper
{
internal static MemoryStream GetFileContents(System.Windows.IDataObject dataObject, int index)
{
//cast the default IDataObject to a com IDataObject
IDataObject comDataObject;
comDataObject = (IDataObject)dataObject;
System.Windows.DataFormat Format = System.Windows.DataFormats.GetDataFormat("FileContents");
if (Format == null)
return null;
FORMATETC formatetc = new FORMATETC();
formatetc.cfFormat = (short)Format.Id;
formatetc.dwAspect = DVASPECT.DVASPECT_CONTENT;
formatetc.lindex = index;
formatetc.tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_HGLOBAL;
//create STGMEDIUM to output request results into
STGMEDIUM medium = new STGMEDIUM();
//using the com IDataObject interface get the data using the defined FORMATETC
comDataObject.GetData(ref formatetc, out medium);
switch (medium.tymed)
{
case TYMED.TYMED_ISTREAM: return GetIStream(medium);
default: throw new NotSupportedException();
}
}
private static MemoryStream GetIStream(STGMEDIUM medium)
{
//marshal the returned pointer to a IStream object
IStream iStream = (IStream)Marshal.GetObjectForIUnknown(medium.unionmember);
Marshal.Release(medium.unionmember);
//get the STATSTG of the IStream to determine how many bytes are in it
var iStreamStat = new System.Runtime.InteropServices.ComTypes.STATSTG();
iStream.Stat(out iStreamStat, 0);
int iStreamSize = (int)iStreamStat.cbSize;
//read the data from the IStream into a managed byte array
byte[] iStreamContent = new byte[iStreamSize];
iStream.Read(iStreamContent, iStreamContent.Length, IntPtr.Zero);
//wrapped the managed byte array into a memory stream
return new MemoryStream(iStreamContent);
}
}
Now you can enumerate the streams in the file contents:
var fileDescriptor = (MemoryStream)Clipboard.GetDataObject().GetData("FileGroupDescriptorW");
var files = FileDescriptorReader.Read(fileDescriptor);
var fileIndex = 0;
foreach (var fileContentFile in files)
{
if ((fileContentFile.FileAttributes & FileAttributes.Directory) != 0)
{
//Do something with directories?
//Note that directories do not have FileContents
//And will throw if we try to read them
}
else
{
var fileData = ClipboardHelper.GetFileContents(Clipboard.GetDataObject(), FileIndex);
fileData.Position = 0;
//Do something with the fileContent Stream
}
fileIndex++;
}

Categories

Resources