Call method in already running .NET assembly from different app - c#

I don't know if you can do this, but basicly I need to be able to call a method in already running .NET process from a different assembly that don't share the same process.
Basically what I have is a application and when it calls a .net method it loads the assembly that contains that method into into a appdomian and then calls the method.
I need to get from the loaded assembly into another process and call a supplied method.
I know this might not help but this is picture of what happens:
alt text http://img527.imageshack.us/img527/6960/probt.jpg
Sorry for the low quality.

Do you have control over the apps? If so, just have them 'open a channel' to each other. Perhaps you can just use a socket, to write to one from the other, or a named pipe, or something similar. It'd what I'd do, if I was writing both of them.

The easiest way is to use ServiceHost to expose a WCF service. You can not directed access an object from a process from another process. The only option is to send a message to the other process interpret the message and execute a action.
Here you can find a quick example:
http://blogs.microsoft.co.il/blogs/davids/archive/2009/05/31/a-hands-on-lab-for-wcf-moc-6461.aspx
Hope this helps.

This question has been marked as a duplicate of this. And people that decided that say in this question there is the answer for that question. Well, this question does not have any accepted answer yet. An may be my answer may help. This solution works with WPF but its essence is indepent of WPF indeed
I have researched a little about the topic since it seems interesting. I think you could do this by using Win32. I have made a very very simple sample. Two WPF applications, first named WpfSender and second named WpfListener. WpfSender will send a message to WpfListener process.
WpfSender only has one button that send the message once it is clicked. WpfListener is only an empty window that will show a message box when receiving the message from WpfSender.
Here is the code behind for WpfSender
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
namespace WpfSender
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var process = Process.GetProcessesByName("WpfListener").FirstOrDefault();
if (process == null)
{
MessageBox.Show("Listener not running");
}
else
{
SendMessage(process.MainWindowHandle, RF_TESTMESSAGE, IntPtr.Zero, IntPtr.Zero);
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
private const int RF_TESTMESSAGE = 0xA123;
}
}
You use Win32 api for sending messages across windows applications
Here is the code for WpfListener
using System;
using System.Windows;
using System.Windows.Interop;
namespace WpfListener
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(WndProc);
}
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == RF_TESTMESSAGE)
{
MessageBox.Show("I receive a msg here a I can call the method");
handled = true;
}
return IntPtr.Zero;
}
private const int RF_TESTMESSAGE = 0xA123;
}
}
I don not write here the XAML since it is very simple. Again this is a very simple sample that shows you how to achieve cross application message sending. The limit is your imagination. You can declare many int constants each one representing an action, an then in a switch statement you can call selected action.
I have to say that I follow two articles that I found in my research:
For knowing how to handle WndProc in Wpf
For knowing how to send messages using win32 api
Hope this helps!

Related

SendMessage WM_SETTEXT to TextBox doesn't trigger TextChanged Event

I have code that gets the handle to a textbox control and uses the windows API to change the text.
The TextChanged event doesn't fire when the text is updated.
Is there a way to fire the TextBox.TextChanged event using the Windows API?
[Update]
I think the reason the event doesn't fire is because the textbox handle is sent though a DCOM interface.
The program is a National Instruments TestStand shell written in c# and uses the NI TestStand COM object for the core functionality. In the TS sequence file (a sort of TS script language) I created an object reference for the textbox handle and set it using the TS api in the shell form's load event. After that I send the handle to my c# DLL. I use SendMessage to update the textbox and that works good. The problem is that the TextChanged event doesn't fire.
I tried using the TS interface to send the textbox and the TextChanged delegate and I couldn't get it to work. I think there is an AppDomain issue doing that through the TS COM object.
As this program proves, the TextChanged event does fire when the control is sent a WM_SETTEXT message.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
const uint WM_SETTEXT = 0x000C;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, unit Msg,
IntPtr wParam, string lParam);
public Form1()
{
InitializeComponent();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
MessageBox.Show(textBox1.Text);
}
private void button1_Click(object sender, EventArgs e)
{
SendMessage(textBox1.Handle, WM_SETTEXT, IntPtr.Zero,
textBox1.Text + ", " + textBox1.Text);
}
}
}
Note that this original version of the answer was overly complex and used a SendMessage like this:
static extern IntPtr SendMessage(IntPtr hWnd, unit Msg,
IntPtr wParam, IntPtr lParam);
and consequently had to perform manual marshalling:
IntPtr text = Marshal.StringToCoTaskMemUni(textBox1.Text + ", "
+ textBox1.Text);
SendMessage(textBox1.Handle, WM_SETTEXT, IntPtr.Zero, text);
Marshal.FreeCoTaskMem(text);
Comments at this question (Automatic casting for string DllImport arguments vs Marshal.StringToCoTaskMemUni) persuaded me to update.
I'm not sure what text you're trying to change but I used a combination of PostMessages and keypresses (for numbers) into the textbox and it triggers the TextChangedEvent.
Look at method 2 of this. It basically sets the mouse to click on the textbox and then send key presses of the text you want to the textbox (letter by letter).

How can I call from one app to another simply?

I'm new to C# and programming. I have 2 apps with different processes and I need to be able to call a method of 1 app from another as simply as possible. I don't need to exchange data or anything else, just need to call a method. I googled about it. I know there are many different ways like pipes, but I need the simplest one. I also found I can send message like this:
const uint WM_COPY = 0x301;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, int lParam);
Process p = Process.GetProcessesByName("appname").FirstOrDefault();
if(p != null)
{
IntPtr hWnd = p.MainWindowHandle;
SendMessage(hWnd, WM_COPY , 0, 0);
}
And I was able to receive the message with this code:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_COPY)
{
//my code
}
else
{
base.WndProc(ref m);
}
}
But there is a problem. App that should receive the message don't have a form, it's just a process. I've used this.Hide(), so MainWindowHandle won't work.
Is it possible to send a message to an app with a hidden form? Or maybe there is a better way to do call a method on the second app? Thanks.
Well, if all you need to do is call a single method with no parameters at all, the easiest would be to create a named Mutex, set it from the calling process and check it from the receiving process.
If you need something more elaborate, take a look at WCF .

Send emulated events to another window

I send mouse events to another application in the following way. The problem is, this works for some applications but not for others.
Why?
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
private const int downclick = 0x201;
private const int upclick = 0x202;
IntPtr handle = IntPtr.Zero;
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
foreach (Process p in Process.GetProcessesByName("mspaint"))
{
IntPtr handle = p.MainWindowHandle;
int X = 50;
int Y = 380;
IntPtr lParam = (IntPtr)((Y << 16) | X);
IntPtr wParam = IntPtr.Zero;
SendMessage(handle, downclick, wParam, lParam);
SendMessage(handle, upclick, wParam, lParam);
}
}
}
}
Using Spy++ I see that the application recieves the following data:
<00062> 0004052C S WM_LBUTTONDOWN fwKeys:0000 xPos:50 yPos:380
<00063> 0004052C R WM_LBUTTONDOWN
<00064> 0004052C S WM_LBUTTONUP fwKeys:0000 xPos:50 yPos:380
<00065> 0004052C R WM_LBUTTONUP
I assume that the events themselves are correct. But I don't know why it works for some software but not for others. How can I send mouse messages from one window to another?
The software where I want to send the messages is not always visible.
Is it possible at all?
No it's not possible in any reliable way - as you've found out in your testing. The mouse messages are only one part of the input. Windows keeps an input state and just sending messages will not update that input state. And you're also ignoring mouse move messages, etc.
For example in your WinForms application you can use the MousePosition property to get the current mouse positon. Sending messages can't simulate that.
Also you can't send the mouse message to the main window handle, you would have to find the exact button you want to click on and send the message directly to the correct button.
So maybe it will work if the application is only listening for mouse messages this will work, but if not they it won't.
They supported way to simulate mouse clicks, is the SendInput function. But that won't work with minimized applications. It literally goes through the entire Windows input process and will move the mouse cursor - which means that the application has to be visible on the screen.
Here's some information, it talks about keyboard events, but similar logic applies:
http://blogs.msdn.com/b/oldnewthing/archive/2005/05/30/423202.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2010/12/21/10107494.aspx

Capturing data from a window in a closed-source third-party Win32 application

I'm planning on creating a C# Windows Forms app as an extension for a third-party Win32 application but I'm stumped as to how to do this right now. The farthest I've gotten is knowing it involves Win32 Hooking and that there's this open source project called EasyHook that's supposed to allow me to do this.
I'd like to know how I can get the text from a textbox or some other data from a control in a third-party Win32 application. The text/data in a control is to be captured from the external application's running window the moment the user presses a button.
I guess the question can be summed up as follows:
How do you determine the event to
hook to when the user clicks a certain button?
How do you get the value
displayed by a Win32 control at the time the button is clicked?
for example I created a application named 'Example' for you.then added a textbox.
it's a c# application but you can use this method for all Win32 applications too.
First create a C# application named Example then add a textbox to it.
you need to textbox's class name to use this application.then create a application and
paste these codes.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, StringBuilder lParam);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr Parent,IntPtr child, string classname, string WindowTitle);
const int WM_GETTEXT = 0x00D;
const int WM_GETTEXTLENGTH = 0x00E;
private void Form1_Load(object sender, EventArgs e)
{
Process procc = GetProcByName("Example");
// You can use spy++ to get main window handle so you don't need to use this code
if (procc != null)
{
IntPtr child = FindWindowEx(procc.MainWindowHandle, IntPtr.Zero, "WindowsForms10.EDIT.app.0.2bf8098_r16_ad1", null);
// Use Spy++ to get textbox's class name. for me it was "WindowsForms10.EDIT.app.0.2bf8098_r16_ad1"
int length = SendMessage(child, WM_GETTEXTLENGTH, 0, 0);
StringBuilder text = new StringBuilder();
text = new StringBuilder(length + 1);
int retr2 = SendMessage(child, WM_GETTEXT, length + 1, text);
MessageBox.Show(text.ToString());
// now you will see value of the your textbox on another application
}
}
public Process GetProcByName(string Name)
{
Process proc = null;
Process[] processes = Process.GetProcesses();
for (int i = 0; i < processes.Length; i++)
{ if (processes[i].ProcessName == Name)proc = processes[i]; }
return proc;
}
}
}
Hope this help.

Embedding a File Explorer instance in a Windows Forms application form

My (C#, .NET 3.5) application generates files and, in addition to raising events that can be caught and reacted to, I want to display the target folder to the user in a form. The file-list is being shown within the same form as other information.
I'm using an instance of the WebBrowser control (System.Windows.Forms.WebBrowser), then navigating to the folder. This shows some default view of the explorer window, with the file summary panel on the left and the files in the 'Tiles' (large icon and text) view.
For example,
wb.Navigate(#"c:\path\to\folder\");
I'd like to suppress the panel and to view the file list in the Details view. The user can get to this via a right-click, context menu, but I'd like it to come up automatically.
I'd rather not have to build my own TreeView, DataGridView or whatever; the WebBrowser control does all the updating and re-sorting and whatnot 'for free'.
Is there a better way? A different control to use or some additional arguments to pass to the control?
And if I could trap events (for example, files being selected/renamed/double-clicked, etc.) then all the better!
WARNING: Long post with lots of code.
When you navigate the web browser control to a file system folder the web browser control hosts a shell view window that in turn hosts the explorer list view. In fact this is exactly the same thing that the Explorer process does as well as the file dialogs and Internet Explorer. This shell window is not a control so there are no methods that can be called on it or events that can be subscribed to but it can receive windows messages and it can be sub-classed.
It turns out that the part of your question dealing with setting the view to Details automatically is actually quite easy. In your web browser control's Navigated event simply find the handle to the shell view window and send it a WM_COMMAND message with a particular shell constant (SHVIEW_REPORT). This is an undocumented command but it is supported on all Windows platforms up to and including Windows 2008 and almost certainly will be on Windows 7. Some code to add to your web browser's form demonstrates this:
private delegate int EnumChildProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg,
IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern int EnumChildWindows(IntPtr hWndParent,
EnumChildProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName,
int nMaxCount);
private const int WM_COMMAND = 0x0111;
private const int SHVIEW_REPORT = 0x702C;
private const string SHELLVIEW_CLASS = "SHELLDLL_DefView";
private IntPtr m_ShellView;
void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
m_ShellView = IntPtr.Zero;
EnumChildWindows(webBrowser1.Handle, EnumChildren, IntPtr.Zero);
if (m_ShellView != IntPtr.Zero)
{
SendMessage(m_ShellView, WM_COMMAND, (IntPtr)SHVIEW_REPORT, (IntPtr)0);
}
}
private int EnumChildren(IntPtr hwnd, IntPtr lParam)
{
int retval = 1;
StringBuilder sb = new StringBuilder(SHELLVIEW_CLASS.Length + 1);
int numChars = GetClassName(hwnd, sb, sb.Capacity);
if (numChars == SHELLVIEW_CLASS.Length)
{
if (sb.ToString(0, numChars) == SHELLVIEW_CLASS)
{
m_ShellView = hwnd;
retval = 0;
}
}
return retval;
}
Every time the web browser navigates to a new window (including when a folder is opened from within the explorer view) a new shell view window is created so the message must be re-sent to the new window in every Navigated event.
For the second part of your question you would like to receive events from the explorer list view. This is quite a bit more difficult than the first part. To do this you would need to sub-class the list view window and then monitor the windows messages for ones that interest you (such as WM_LBUTTONDBLCLK). In order to sub-class a window you would need to create your own class derived from the NativeWindow class and assign it the handle of the window that you need to monitor. You can then override its Window procedure and handle the various messages as you wish. Below is an example of creating a double click event - it is relatively simple but to get extensive access to the explorer list view may involve a lot more work than you are willing to do.
Add this to your form:
private ExplorerListView m_Explorer;
void OnExplorerItemExecuted(object sender, ExecuteEventArgs e)
{
string msg = string.Format("Item to be executed: {0}{0}{1}",
Environment.NewLine, e.SelectedItem);
e.Cancel = (MessageBox.Show(msg, "", MessageBoxButtons.OKCancel)
== DialogResult.Cancel);
}
and these two lines to the Navigated event handler (right after the SendMessage):
m_Explorer = new ExplorerListView(m_ShellView);
m_Explorer.ItemExecuted += OnExplorerItemExecuted;
Then add the following classes:
class ExplorerListView : NativeWindow
{
public event EventHandler<ExecuteEventArgs> ItemExecuted;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg,
IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent,
IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
private const int WM_LBUTTONDBLCLK = 0x0203;
private const int LVM_GETNEXTITEM = 0x100C;
private const int LVM_GETITEMTEXT = 0x1073;
private const int LVNI_SELECTED = 0x0002;
private const string EXPLORER_LISTVIEW_CLASS = "SysListView32";
public ExplorerListView(IntPtr shellViewHandle)
{
base.AssignHandle(FindWindowEx(shellViewHandle, IntPtr.Zero,
EXPLORER_LISTVIEW_CLASS, null));
if (base.Handle == IntPtr.Zero)
{
throw new ArgumentException("Window supplied does not encapsulate an explorer window.");
}
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_LBUTTONDBLCLK:
if (OnItemExecution() != 0) return;
break;
default:
break;
}
base.WndProc(ref m);
}
private int OnItemExecution()
{
int cancel = 0;
ExecuteEventArgs args = new ExecuteEventArgs(GetSelectedItem());
EventHandler<ExecuteEventArgs> temp = ItemExecuted;
if (temp != null)
{
temp(this, args);
if (args.Cancel) cancel = 1;
}
return cancel;
}
private string GetSelectedItem()
{
string item = null;
IntPtr pStringBuffer = Marshal.AllocHGlobal(2048);
IntPtr pItemBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(LVITEM)));
int selectedItemIndex = SendMessage(base.Handle, LVM_GETNEXTITEM, (IntPtr)(-1), (IntPtr)LVNI_SELECTED).ToInt32();
if (selectedItemIndex > -1)
{
LVITEM lvi = new LVITEM();
lvi.cchTextMax = 1024;
lvi.pszText = pStringBuffer;
Marshal.StructureToPtr(lvi, pItemBuffer, false);
int numChars = SendMessage(base.Handle, LVM_GETITEMTEXT, (IntPtr)selectedItemIndex, pItemBuffer).ToInt32();
if (numChars > 0)
{
item = Marshal.PtrToStringUni(lvi.pszText, numChars);
}
}
Marshal.FreeHGlobal(pStringBuffer);
Marshal.FreeHGlobal(pItemBuffer);
return item;
}
struct LVITEM
{
public int mask;
public int iItem;
public int iSubItem;
public int state;
public int stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public IntPtr lParam;
public int iIndent;
public int iGroupId;
int cColumns; // tile view columns
public IntPtr puColumns;
public IntPtr piColFmt;
public int iGroup;
}
}
public class ExecuteEventArgs : EventArgs
{
public string SelectedItem { get; private set; }
public bool Cancel { get; set; }
internal ExecuteEventArgs(string selectedItem)
{
SelectedItem = selectedItem;
}
}
This should give you an idea of what you would need to do. If you want more than fairly simple events you may want to look for a alternative control, though from what I have seen in the free and low cost areas there are some pretty decent controls but they all have some quirks and will not give a seamless explorer experience.
Remember this code was put together fairly quickly without error handling or comments and ignoring several issues such as multiple selected items, so use it as a guideline and at your own risk.
In order to handle renaming, deleting and make other customization you need to write your own file explorer. WebBrowser control is not suitable for your needs. It's just a wrapper over ActiveX component.
You should check this codeproject article. It contains an implementation of file explorer. There are few more samples of file browser:
one
two
LogicNP Software has two controls (FileView and ShComboBox) that do what your looking for:
http://www.ssware.com/fldrview.htm
You can download a trial from their page, however it's ~130$ for the license.
I have written a library that might be able to help you. You can find it at: http://gong-shell.sourceforge.net/
The control you're looking for is the ShellView. There's tutorials there on how to create a simple Windows Explorer clone in only a few lines too.
Note for .NET 4.0 users: Gong-shell is currently broken for 4.0. The framework introduced changes in Interop and it will build just fine but cause different issues when interfacing with shell32 (notably the shellicon api, leading to an unmanaged null pointer dereference).
Check out this article here, it shows how to do this in .NET and WinForms. Doing it this way gives full-control over what the user sees.
I've used it in one of my applications and it works really well. You can show icon/details/list view and it stops the user moving to other directories (which is often the problem of showing the standard file/directory dialogs.
I use it to show the screen like the one below below http://img7.imageshack.us/img7/7647/screenshotbaf.png:
You may want to look at the ExplorerBrowser object.
See http://blogs.msdn.com/ieinternals/archive/2009/12/30/Windows-7-Web-Browser-Control-will-not-browse-file-system.aspx for more details.
If you are happy being Windows Vista only and wrapping a COM control, IExplorerBrowser might be acceptable for you needs.
This The Code Project article shows its use within an MFC program but at least one other person seems to have got it to work in C# after some effort.
The newer API exposes considerably more programmability than simply intercepting messages, but it is (obviously) useless for older platforms.
If you want to open a different window to display the target folder's content you can use System.Windows.Forms.OpenFileDialog, or SaveFileDialog, or inherit from FileDialog and extend it.
To allow the user to select a folder you can use FolderBrowserDialog, though as a user I don't like that control.
Does this help or you absolutely have to embed a control in your form?
Asaf

Categories