Clipboard transfer between apps - c#

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?

Related

How can I edit the string contents of a rich text item in the clipboard in C#?

I am working on automating a process within my business, part of which is sending an email through SalesForce. We don't have access to the SF API and the email has to be sent through salesforce in order to keep the communication searchable for the coworkers.
I need to use a template which can be selected in SalesForce, however this function does not work in IE (which our RPA solution uses) so I need to build this email from scratch.
I see two options for this:
Use the HTML to recreate the format with the right variables. This entails inserting/injecting/manipulating HTML.
Copy the format into memory/the clipboard, edit it programatically and paste it into the SF interface
This question will be about option 2. I have posted an additional question with regards to the first option separately. It can be found here.
Now on to the question: I found that I can copy the text in the template from the SalesForce webpage to the clipboard (using CTRL+C) and then paste it into word, doing so will preserve the formatting and layout of the text as well as included images. I can then copy and paste this back from word to the SF webpage as well preserving the images, format and layout. This means that the formatting and image data is retained in the clipboard.
Now I need to edit the template text before I paste it into the webpage in order to use this as a solution for automation. I have been experimenting with this for the past week but I can't find a way to edit the text while preserving the formatting and layout.
I use C# and know how to GetText and SetText as wel as images (separately) from the clipboard. However the text is then a plain string without any layout and formatting. I want to replace certain keywords in the template with the required variables.
At this point I have the following code to investigate the contents of the clipboard:
// Initialise a DataObject
DataObject t = null;
try
{
// Get Data from clipboard
t = (DataObject)Clipboard.GetDataObject(); // GetData(DataFormats.Html);
// Get formats in the DataObject
string[] tFormats = t.GetFormats();
// For each format that was found create a separate object (I did this manually, I
// inspected the formats earlier by eye.
object objectDescriptor = t.GetData("Object Descriptor", true);
object rtf = t.GetData("Rich Text Format", true);
object HTMLFormat = t.GetData("HTML Format", true);
object sysString = t.GetData("System.String", true);
object unicodeText = t.GetData("UnicodeText", true);
object text = t.GetData("Text", true);
object enhancedMetafile = t.GetData("EnhancedMetafile", true);
object metaFilePict = t.GetData("MetaFilePict", true);
object embedSource = t.GetData("Embed Source", true);
object linkSource = t.GetData("Link Source", true);
object linkSourceDescriptor = t.GetData("Link Source Descriptor", true);
object objectLink = t.GetData("ObjectLink", true);
// Try replacing the text
HTMLFormat = (object)HTMLFormat.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("HTML Format", HTMLFormat);
sysString = (object)sysString.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("System.String", sysString);
unicodeText = (object)unicodeText.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("UnicodeText", unicodeText);
text = (object)text.ToString().Replace("|KeyWord_A|", "Value_A");
t.SetData("Text", text);
Clipboard.SetDataObject(t);
}
catch (exception ex)
{
// Do exception handling
}
Inspecting all these objects I see that, even though they are apparently included in the DataObject, they are all null, except for HTML Format, System.String, UnicodeText and Text. Within these options the HTML Format data is the only one that appears to have some formatting data in there (I don't know where the included image is stored though). I am able to replace the keyword in that HTML Format succesfully, however if I set it back into the DataObject and then extract the HTML format again nothing has changed. The same goes for the other text items.
I also tried changing the text in the same way for all the text items. This however has the same results. If I then try to paste the clipboard contents into Word it freezes for a little while and then nothing happens.
How can I edit this DataObject? And will that actually translate into a properly formatted and layout-ed text including the image and hyperlinks, or is this approach a dead end?
Cheers!

How to identify a string as filepath and open in file explorer when clicked?

I am relatively new to C#/Visual Studio 2015 application development, coming from Android. I am writing a chat application that also allows users to send files to each other. The file transfer functionality is in place; the file gets downloaded to a pre-set folder when received, and the file-path of that file is then shown in the chat box to the recipient. However, that file path is shown as though it were regular text.
How do I make it such that said file-path (and/or urls, ideally) appear as a clickable hyperlinks, that then open said file?
Any help or resources to be pointed to would be most appreciated!
If you create a linkLabel object to show the path, you can add a callback to the event LinkClicked and open a file explorer:
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Process.Start("C:/");
}
Here we go:
You should use Uri class to build your url from string:
string filePath = "C:\\example.txt";
Uri uri = new Uri(filePath);
return uri.AbsoluteUri;
Hope it helps;)

Get full file path including file's name from an uploaded file in C#

I have this web application project which requires a better user-interface and I am doing it by C#.
I already have my html file with JS done but I need some data from user.
In my JS embedded in HTML file, I used the code below to find the file on local driver and get the data from that excel file and then put all these data into an array.
var excel = new ActiveXObject("Excel.Application");
var excel_file = excel.Workbooks.Open(file1);
var excel_sheet = excel.Worksheets("Sheet1");
However, the "file1" you see above seems to require a full name path,say "C:\test.xls" in my case.
I am new to C# and just built a button on my form design, by clicking the button, I seem to be able to browse my local file.
private void button1_Click(object sender, EventArgs e)
{
int size = -1;
DialogResult result = openFileDialog1.ShowDialog(); // Show the dialog.
if (result == DialogResult.OK) // Test result.
{
string file = openFileDialog1.FileName;
try
{
string text = File.ReadAllText(file);
size = text.Length;
}
catch (IOException)
{
}
System.Diagnostics.Process.Start(file);
}
Console.WriteLine(size); // <-- Shows file size in debugging mode.
Console.WriteLine(result); // <-- For debugging use.
}
So, my question:
How can I get this kind of full file path of an uploaded file in C# ?
And furthermore, it would be awesome if some one can tell me how to get this value into my javascript or HTML!
Thank you in advance
You won't be able to depend on getting the full path. In the end, the browser only needs to multi-part encode the physical file and submit it as a form post (of sorts). So once it has it's location and has encoded it -- it's done using it.
It's considered a security risk to expose the file structure to Javascript/HTML (ie., the internet).
Just an update.
I used another logic and it worked as expected. Instead of getting absolute file path , I managed to open the file , save it as somewhere and make my html call find that file path no matter what.

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 backup and restore the system clipboard in 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);

Categories