I have a two office add-in which communicates using clipboard, for example: copy and paste table or picture from excel to word. While copy\paste operation running, user can corrupt data in clipboard if he copy something in clipboard. I tried lock clipboard after copy in first application and unlock clipboard before paste in second application.
I can lock Clipboard in one application, but can't unlock it in another application
This is class with wrappers for Clipboard
public class MyClipboard
{
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool OpenClipboard(IntPtr hwnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseClipboard();
public void Close()
{
CloseClipboard();
}
public void Open()
{
OpenClipboard(IntPtr.Zero);
}
}
First application :
MyClipboard clipboard = new MyClipboard();
clipboard.Open();
...
Second application :
MyClipboard clipboard = new MyClipboard();
clipboard.Close();
...
What should I do to Open clipboard from one application, and close clipboard from another application?
Thanks!
Rather than using the clipboard to share data between your apps (as you say the user could modify the clipboard) I'd use something like a
Shared known file (with some file watching) could be slow
Named Pipes (could be over complex)
WCF (massive overkill)
Memory Mapped files (Could be hard in .NET) http://msdn.microsoft.com/en-us/library/ms810613.aspx
Here is someothers ideas..
What is the simplest method of inter-process communication between 2 C# processes?
If you really want to use the clipboard you could put in some extra custom attributes to the format to make sure that user has not used the clipboard themselves. But it seems like bad practice to use the clipboard in this way, but that's my opion.
HTH.
David
Related
This question already has answers here:
get the titles of all open windows
(6 answers)
Closed 6 years ago.
i wonder if its possible to create application that somehow look through all opened in windows(7,8,10) system forms(not only main forms but also its inside open forms) ? eg i have some application open and in it i have some forms is it possible to check if form of certain type is opened (or form that have ceratin text in form.text property. Or any other idea how to get info about what forms in application are open ? :)
I once have seen application, create by some guy, that app can open certain windows and click certain buttons in other application. that guy can't know what are name for forms or its buttons for that other application, so he must have checeked somehow which forms are opened in that other app and what controls it has.
So another question is how to check controls on opened forms (especiali i need to read some data from certain text fields from form that has certain text in header of window. ) . I pressume he use winapi but could you provide a help how to do that from c#?
Thanks for help:)
Have a look at EnumWindows function
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633497(v=vs.85).aspx
And you can enumerate all windows (given by their handles - HWND) like that:
using System.Runtime.InteropServices;
....
[return: MarshalAs(UnmanagedType.Bool)]
private delegate Boolean EnumerationCallback(IntPtr handle, IntPtr parameter);
[DllImport("User32.dll",
CallingConvention = CallingConvention.Winapi,
ExactSpelling = true,
EntryPoint = "EnumWindows",
CharSet = CharSet.Unicode,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern Boolean EnumWindows(
EnumerationCallback lpEnumFunc,
IntPtr lParam);
...
EnumWindows((handle, p) => {
//TODO: put relevant code here
return true;
},
IntPtr.Zero);
You may want EnumChildWindow API function as well
https://msdn.microsoft.com/en-us/library/windows/desktop/ms633494(v=vs.85).aspx
I am working on an application which on the click of a button will clear both the system clipboard and the office clipboard.
I have tried System.Windows.Forms.Clipboard.Clear() and the following
static class WinAPI
{
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool OpenClipboard(System.IntPtr WinHandle);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool EmptyClipboard();
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool CloseClipboard();
public static void ClearClipboard()
{
if (OpenClipboard(System.IntPtr.Zero))
{
EmptyClipboard();
CloseClipboard();
}
}
}
Both of them seem clear only the system clipboard. Is there a way to extend this to office clipboard.
Per this question, the answer appears to be "no, not cleanly":
There has been no VBA support for manipulating the Office clipboard
after Excel 2000. SendKeys is flaky, but it's the only way.
The "SendKeys" referred to is a "solution" where you send the necessary keystrokes or window messages -- assuming one is running, if not, you'll have to start one. There are also other ways to do that, like AutoIt. Unfortunately, all of these solutions are fragile and prone to break between versions of Office, because they rely on internal details of window names and appearances. Before you do this, make sure you're willing to accept the maintenance burden.
A better approach is probably telling whoever wants this application that it can't be done because Microsoft wants clipboard control to remain with the end user -- or, alternatively, that there already is a button for clearing the clipboard, and it exists in Office (Alt, H, FO shows it in my version). That doesn't always work, but when it does it saves you a ton of work.
You could copy a single space to the clipboard, if the other methods are troublesome?
You can try the solution presented here. It's meant for VBA but you can easily convert the code to C#.
Let me know if you need help.
This is the method definition:
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr lparam, IntPtr wparam);
This is the call to SendMessage:
//WM_COPY = 0x0301
SendMessage(handle, WM_COPY, IntPtr.Zero, IntPtr.Zero);
This is how I am retrieving the data:
string text = System.Windows.Forms.Clipboard.GetText();
I'd like to do the same thing except that I don't want the data to be copied to the clipboard. Is it possible to copy the data to some other part in memory? If so, how?
No. You can't control what other application will do when it receives a message. You get whatever behavior target window has for that message and nothing else (unless you control the target too, than you can handle it yourself).
WM_COPY is just a message (also it is standard one and make sense to be handled in particular way) - some windows will handle it, some will not. As defined on MSDN WM_COPY will save text for edit control.
An application sends the WM_COPY message to an edit control or combo box to copy the current selection to the clipboard in CF_TEXT format.
If you are implementing your own application that handles WM_COPY you can copy data wherever you want, also if sub-classing edit controls it would make a lot of sense to keep default behavior...
You could write it to a file instead if the clipboard is not an option.
I'm running a small tool (on Windows 7, 32 Bit) that I would like to see what document is open in another application I've tried this, which works for NotePad on Windows.
var myProcess = Process.GetProcessesByName("NotePad");
string title = myProcess[0].MainWindowTitle;
MessageBox.Show(title);
the output is:
"New Text Document - Notepad"
Now, if I try another application it doesn't always give me the correct title but I noticed that most microsoft applications seem to be fine - NotePad, WordPad, EXCEL etc. It's this other software that is an issue. It has a long title but just returns a very simple name.
Here's what I get from my application which has processName = "FooBar"
The actual running window has this up the top:
"FooBar Software Verson 1.2 - [Results]"
and my code gives:
"FooBar"
any ideas?
[EDIT} 2012-11-19
The very crux of this issue is that I was trying to get the name of the open file from the window. It now seems that the software I'm using doesn't display it there. What I've discovered is that a program called "AutoIT3 Window Spy" can get the text I need as the text of the open file is on the window and not only in the title. I downloaded the source (it's part of http://www.autohotkey.com/ which is open source. It seems to rely on many of the suggestions already made but I'm not able to figure it out just yet.) The source code that I"m looking at is c++ and is located here https://github.com/AutoHotkey/AutoHotkey
So I think the solution to my problem may lay elsewhere. This one may go unanswered.
The main window title is what you see when you go in to task manager and look at the Description column, not the window title itself.
It's the title of the process, not the title of a particular window in the process. A given process may have any number of windows open at one time.
If you need the actual window title, you have to hook user32 something like this:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Security;
namespace Application
{
public class Program
{
public static void Main ( )
{
IntPtr hwnd = UnsafeNativeMethods.FindWindow("Notepad", null);
StringBuilder stringBuilder = new StringBuilder(256);
UnsafeNativeMethods.GetWindowText(hwnd, stringBuilder, stringBuilder.Capacity);
Console.WriteLine(stringBuilder.ToString());
}
}
[SuppressUnmanagedCodeSecurity]
internal static class UnsafeNativeMethods
{
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int GetWindowText ( IntPtr hWnd, [Out] StringBuilder lpString, int nMaxCount );
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr FindWindow ( string lpClassName, string lpWindowName );
}
}
It's possible that the 'title' you're seeing is some kind of owner-drawn UI element which is overriding the visual representation of the title, while the Windows APIs are likely to ignore that sort of thing.
I'd recommend examining the window with a tool like Spy++ to see if that's the case.
It's also possible that the author of the application decided to override the WM_GETTEXT message and is returning that instead of what's actually in the titlebar, although I'm not 100% sure if GetWindowText() is being called here and whether or not it sends the message explicitly or works some other way.
The developer has stated the following:
"I think the failure may be due to a discontinued property Form.Caption in VB 6.0 that was replaced with a Form.Text in. NET"
Thank you all for your valuable suggestions along the way!
I need to interact with an external application running, and send specific keypresses & releases. I've tried to use the SendKeys class, but it does only half of the job, as the keypress is being sent with an immediate keyrelease to the external applications.
I need to be able to simulate a "key hold down" for the external app. I'm now trying to use the SendMessage thing, but for now it won't work at all :( and I don't even get errors.
Ok, case solved. I actually installed VC++ to try the core keybd_event() function, and after it worked I was able to use it wisely in C#.
Here's the code, and surprisingly it's very simple. You'll need to add this using to your code to be able to import dll's: using System.Runtime.InteropServices;
This code will press and hold the '1' button for 3 secs, and then will release for 1 second and repeat the process.
(the code highlight got messed up :/, copy from 'namespace ...' to the last bracket '}')
public class Program
{
[DllImport("user32.dll")]
private static extern void keybd_event(byte bVk, byte bScan,
uint dwFlags, UIntPtr dwExtraInfo);
private static void Main(string[] args)
{
while (true)
{
keybd_event((byte)0x31, (byte)0x02, 0, UIntPtr.Zero);
Thread.Sleep(3000);
keybd_event((byte)0x31, (byte)0x82, (uint)0x2, UIntPtr.Zero);
Thread.Sleep(1000);
}
}
}
have you tried using PostMessage to send WM_KEYDOWN and WM_KEYUP ?
Edit
You would use it this way (I am writing in C++, but you can easily use PInvoke and ..NET)
HWND hwnd = FindWindow(NULL,_T("Mywindow"));
PostMessage(hwnd,WM_KEYDOWN,VK_A,0);
You can use the WSH Scripting Shell to do this:
var shell = new WshShellClass();
var missing = System.Reflection.Missing.Value;
shell.SendKeys("MOO!!!", ref missing);
All you need to do is add a COM reference to "Windows Scripting Host Object", version 1.0. Everything is in the namespace IWshRuntimeLibrary.
The official API is SendInput.