How do I backup and restore the system clipboard in C#? - c#

I will do my best to explain in detail what I'm trying to achieve.
I'm using C# with IntPtr window handles to perform a CTRL-C copy operation on an external application from my own C# application. I had to do this because there was no way of accessing the text directly using GET_TEXT. I'm then using the text content of that copy within my application. The problem here is that I have now overwritten the clipboard.
What I would like to be able to do is:
Backup the original contents of the clipboard which could have been set by any application other than my own.
Then perform the copy and store the value into my application.
Then restore the original contents of the clipboard so that the user still has access to his/her original clipboard data.
This is the code I have tried so far:
private void GetClipboardText()
{
text = "";
IDataObject backupClipboad = Clipboard.GetDataObject();
KeyboardInput input = new KeyboardInput(this);
input.Copy(dialogHandle); // Performs a CTRL-C (copy) operation
IDataObject clipboard = Clipboard.GetDataObject();
if (clipboard.GetDataPresent(DataFormats.Text))
{
// Retrieves the text from the clipboard
text = clipboard.GetData(DataFormats.Text) as string;
}
if (backupClipboad != null)
{
Clipboard.SetDataObject(backupClipboad, true); // throws exception
}
}
I am using the System.Windows.Clipboard and not the System.Windows.Forms.Clipboard. The reason for this was that when I performed the CTRL-C, the Clipboard class from System.Windows.Forms did not return any data, but the system clipboard did.
I looked into some of the low level user32 calls like OpenClipboard, EmptyClipboard, and CloseClipboard hoping that they would help my do this but so far I keep getting COM exceptions when trying to restore.
I thought perhaps this had to do with the OpenClipboard parameter which is expecting an IntPtr window handle of the application which wants to take control of the clipboard. Since I mentioned that my application does not have a GUI this is a challenge. I wasn't sure what to pass here. Maybe someone can shed some light on that?
Am I using the Clipboard class incorrectly? Is there a clear way to obtain the IntPtr window handle of an application with no GUI? Does anyone know of a better way to backup and restore the system clipboard?

It's folly to try to do this. You cannot faithfully restore the clipboard to its prior state. There could be dozens of unrendered data formats present using "delayed rendering", and if you attempt to render them all, you'll cause the source app to run out of resources. It's like walking into a resturaunt and saying "give me one of everything".
Suppose that the user has selected 500 rows x 100 columns in Excel, and has copied that to the clipboard. Excel "advertises" that it can produce this data in about 25 different formats, including Bitmap. Once you paste it as a Bitmap, you force Excel to render it as a bitmap. That's 50000 cells, and would be a bitmap approx 10,000 x 15,000 pixels. And you expect the user to wait around while Excel coughs that up, along with 24 other formats? Not feasible.
Furthermore, you're going to be triggering WM_DrawClipboard events, which will impact other clipboard viewers.
Give up.

You could save the content of the clipboard in a dictionary, and restore it afterwards :
public IDictionary<string, object> GetClipboardData()
{
var dict = new Dictionary<string, object>();
var dataObject = Clipboard.GetDataObject();
foreach(var format in dataObject.GetFormats())
{
dict.Add(format, dataObject.GetData(format));
}
return dict;
}
public void SetClipboardData(IDictionary<string, object> dict)
{
var dataObject = Clipboard.GetDataObject();
foreach(var kvp in dict)
{
dataObject.SetData(kvp.Key, kvp.Value);
}
}
...
var backup = GetClipboardData();
// Do something with the clipboard...
...
SetClipboardData(backup);

Related

Accessing Images in PowerPoint file via VSTO Add-In (C#)

I am trying to build a PowerPoint add-in that will allow me to do a one-click optimization of the current presentation by removing unused master slides and converting huge 24-bit PNG files to slightly-compressed JPGs.
I have the first part handled already, and now I'm working on the images. Although I can easily find the Shape object containing the image, I cannot find a way to access the source image through the managed API. At best, I can copy the shape to the clipboard, which does give me the image but in a different format (MemoryBmp).
using PPTX = Microsoft.Office.Interop.PowerPoint;
...
foreach (PPTX.Slide slide in Globals.ThisAddIn.Application.ActivePresentation.Slides)
{
foreach (PPTX.Shape shape in slide.Shapes)
{
if (shape.Type == MsoShapeType.msoPicture)
{
// Now, how can I access the source image contained within this shape?
// I -can- copy it via the clipboard, like this:
shape.Copy();
System.Drawing.Image image = Clipboard.GetImage();
// ...but image's format reflects a MemoryBmp ImageFormat, as noted here:
// https://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Advanced/ImageFormat.cs
// ...which doesn't help me determine if the source image is something that should be optimized.
}
}
}
Obviously, I can get to the images directly via other methods (e.g. accessing the contents of the file as a ZIP, or using OpenXML SDK), but I'm trying to perform all the steps from within the already-opened presentation (which means I can't update the open file). Any thoughts on how I can do this?
You could use a Microsoft.Office.Interop.PowerPoint.Shape Export function.
void Export(string PathName, PpShapeFormat Filter, int ScaleWidth = 0, int ScaleHeight = 0, PpExportMode ExportMode = PpExportMode.ppRelativeToSlide)
There is a very little documentation of this function.
I've found it in the Shape interface definition.
So your code would look something like:
shape.Export(<some_file>, PpShapeFormat.ppShapeFormatPNG);
More info on MSDN

Xamarin Forms: Default Image if Url not found

In xamarin forms we can create images like this:
Image i = new Image { Source = "http://www.foo.com/foo.jpg };
After adding this to layout if url returns an image it will display it. What I want to now is is there a way to know if ths Url is an actual image. Otherwise I am going to show an default image.
Regards.
Edit
I have created a function:
public string GetImageSourceOrDefault(string orgUrl)
{
var req = (HttpWebRequest)WebRequest.Create(orgUrl);
req.Method = "HEAD";
try
{
using (var resp = req.GetResponse())
{
bool res = resp.ContentType.ToLower(CultureInfo.InvariantCulture)
.StartsWith("image/");
if (res)
return orgUrl;
else
return "defualt_logo.jpg";
}
}
catch
{
return "default_logo.jpg";
}
}
This function does the trick. However, for every image it does a request. I have a listview which shows like 220 entries. Using this method messed up the time that listview gets loaded.
Note: this function is natively called using dependency injection.
Maybe further improvements will do. Any ideas?
FFImageLoading CachedImage supports Loading and Error Placeholders (and much more). It's basically a API compatible replacement for Image with additional properties. You could try that.
var cachedImage = new CachedImage() {
LoadingPlaceholder = "Loading.png",
ErrorPlaceholder = "Error.png"
};
https://github.com/molinch/FFImageLoading
With Xamarin.Forms UriImageSource you can specify different caching length, and whether caching is used by using the properties CacheValidity and CachingEnabled.
By default it will automatically cache results for 1 day on the local storage of the device.
In your function, as you mention, you are downloading the image every single time.
You have no current functionality that is storing and caching the result for later re-use.
By implementing something like this on the platform specific layer would get around your current solution of re-downloading the image every single time.
Alternatively as a workaround, if you didn't want to implement the above, you could try putting two Image controls stacked upon each other, maybe in a Grid, with the bottom image showing a default placeholder image, and on-top another Image control that would show the intended image, if successfully downloaded, using the UriImageSource.
You could also possibly hook hook into the PropertyChange notification of the Image.Source and detect it being set, with the image then being displayed. Upon detection you could then release the image from the temporary place holder Image control perhaps?

Clipboard transfer between apps

Mysterious clipboard issue:
I have an application under development that launches an external application (as a System.Diagnostics.Process object) which is expected to copy some data (text) to the clipboard. When the external app closes, the client application retrieves the text from the clipboard.
The problem is that although once the external application has copied the text to the clipboard I can paste it into, say, notepad, the client app is getting an empty string from the clipboard.
external app code:
private void btn1_Click(object sender, EventArgs e)
{
//copy text pane to clipboard
DataObject obj = new DataObject();
obj.SetData(tbText.Text);
System.Windows.Forms.Clipboard.SetDataObject(obj);
}
Client app code:
string returnedValues = string.Empty;
System.Windows.Forms.IDataObject data = System.Windows.Forms.Clipboard.GetDataObject();
if (data != null && data.GetDataPresent(System.Windows.Forms.DataFormats.Text) == true)
{
returnedValues = (string) data.GetData(System.Windows.Forms.DataFormats.Text, true));
}
The data object is always null, even though the clipboard has the text in it and I can paste it into other applications.
Can anyone point me at the flaw in the client app code? Why is 'data' always null, even though there is data on the clipboard?
When all your clipboard has is an string copied as text, you have to retrieve it as
Clipboard.GetText()
and to retrieve other types of objects you can use GetData()
For text, you really should use SetText() and GetText(). And make sure that overwriting the current content of the clipboard is what the user expects in your scenario. If not, you should use interprocess communication instead, see e.g. What is the simplest method of inter-process communication between 2 C# processes?

Copy Excel RangeSelection to array in Windows Application

Thanks for any help in advance :)
Context
I am using SpreadsheetGear within my Windows Application and there are certain cases where a user will want to copy data from an open Excel application and paste the two dimensional grid into the SpreadsheetGear object in my application.
Motivation
I'm attempting to acquire information from the data in the clipboard in preparation for the paste to happen. The numbers of rows and columns of the data coming in needs to be determined before the paste happens so that the SpreadsheetGear control and other controls on the page are "ready" for the data.
Problem 1
How do I acquire such data from the Clipboard? I'm using
System.Windows.Forms.Clipboard.GetData(...)
but I'm not sure whether I should indicate the DataFormat to be CommaSeparatedValue (CSV) or Text. Will one way or the other work best? Is there another DataFormat that I am overlooking that could help me out?
Problem 2
I used this statement in the Immediate Window of Visual Studio 2012:
System.Diagnostics.Debug.WriteLine(Clipboard.GetText())
Interestingly, this returned a portion of the data I selected and copied in Excel. Is there a limit to the amount of data that the clipboard can handle from Excel? Or is there a way for my Windows App to help allocate more space on the clipboard, knowing the user is about the select data from Excel and copy that data to the clipboard?
Please let me know if I can provide more clarification. I'm a little lost and not sure about the scope of this issue. Thanks!
Here is what ended up working for me:
try
{
var data = Clipboard.GetDataObject();
var stream = (System.IO.Stream)data.GetData(DataFormats.CommaSeparatedValue);
var enc = new System.Text.UTF8Encoding();
var reader = new System.IO.StreamReader(stream, enc);
string data_csv = reader.ReadToEnd();
string[] data_csv_array = data_csv.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
...
}
catch (Exception e)
{
ErrorHandling.ShowError("There is no data on the Clipboard to paste.");=
}
The "..." indicates that I do something pretty particular with the data once I have it in array form. What I wanted to do was display the portion of my solution that would help people in general with a similar need.

How do I remove Excel copy focus with C#

I created a small app using .NET framework 4 which processes info copied from an Excel spreadsheet and saves the processed data to the clipboard.
The problem is that when the app finishes, Excel's focus is still on the cell from which the data was copied.
My info is in the Office clipboard, but I have to manually remove focus before I can paste it. I would like my app to remove the focus for me, in order to simplify the process. How can this be done?
try
{
Clipboard.Clear();
}
catch (Exception)
{
MessageBox.Show("Clipbour Clear Error");
}
try
{
Clipboard.SetText(clipbourholder.ToString(), TextDataFormat.UnicodeText);
}
catch (Exception)
{
MessageBox.Show("Clipbour Paste Error");
}
When information is in clipboard excel has this border around cells, which i dont like because it is active clipboard. I need mine info in active office clipboard.
Active clipbourd example
I found workaround for that. I generated 64k number and puted to clipbourd and then i could pass my real value. It looks like only mine excel had this issue.

Categories