Windows Shell Integration with multiple files - c#

I'm making a program that is using Windows Shell Integration, and the registry changes I do are these:
For example, for .txt, I see that the value for HKEY_CLASSES_ROOT\.txt > (Default) is txtfile. I add to HKEY_CLASSES_ROOT\txtfile\shell the key myprogram > (Default) with the value Open with MYPROGRAM. to myprogram I add command > (Default) with the value *path-to-my-program* %1. Now when I right click a .txt file there is an option to open it with my program.
But when I do that with multiple .txt files Windows opens my program many times with each time another file as argument. But I want to open my program one time with all the files as many arguments. Is there an option to do that with changing stuff in registry?
If not, I also could not find a way to make a program that can be opened multiple times and combine all of them to one, so I can also do it that way if someone can help me with it. I'm making this program with C#, by the way.

You have to make your application "Single Instance".
Something like this should do the trick:
(untested code, just for reference)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace YourApp
{
class Program
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[STAThread]
static void Main()
{
bool createdNew = true;
using (Mutex mutex = new Mutex(true, "MyApplicationName", out createdNew))
{
if (createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 frm = new Form1();
frm.SetNewData("send command line here");
Application.Run(frm);
}
else
{
Process current = Process.GetCurrentProcess();
foreach (Process process in Process.GetProcessesByName(current.ProcessName))
{
if (process.Id != current.Id)
{
SetForegroundWindow(process.MainWindowHandle);
// send message to that form or use .Net remoting
break;
}
}
}
}
}
}
}
For a better example see this CodeProject solution.

The standard way to handle this is to use a mutex to ensure that only a single instance of your program runs. Then when the shell attempts to start up a new instance to open each file, the new instance simply passes the message on to the already running instance and lets it open the file.

One possible option is to check if another instance of your program is already running. If so, you pass the file path to that instance to open. For program inter-communication you can use whatever you like, e.g.: .NET remoting, named pipes, DDE, custom window messages, etc..

That's the default. you have told the shell that when clicking 'open with MYPROGRAM' invoke application %1 for each file that is selected.
The simplest way to fix this is to make your application single instance, and to send messages to the instance that is already running when another file is selected. This way one instance of your application is launched to open one file and it receives a request to open each of the other files. This is how it is generally accomplished using C++
if you look at the Developer Documentation there is also the recommendation to use DDE. I don't know how accessible DDE is from within C#, and it's use is deprecated.

Related

C#: close process on app's process' killing (with taskman)

I'm very new to C# so my question may sound rediculous. I'm developing an application which sometimes need to run ffmpeg. As you guess, this ffmpeg process must be killed when it's host app was closed. I use such code for this task:
AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit);
private void OnProcessExit(object sender, EventArgs e)
{
proc.Kill();
}
This works fine, when the app is closed correctly (via it's interface or with Taskman - Applications). The problem is that OnProcessExit event won't trigger, if the program's process was killed (with Taskman - Processes). As far as I know, killing process and closing program actions are not the same on the low level, but I guess, killing process is a command to it and it can be handled with C# tools. So, is it possible, to close child process in this case?
I think Try this
Application.Exit();
I recommend to use job objects (as per Scott Miller suggestion).
Another option can be have special helper app for your app, which does following:
Start your app
When your app crashed, clean up after it.
But job objects is definitely better option, it specifically made for this
let your host program submit its program ID as parameter,
and then listen if the program exits.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
[STAThread]
static void Main(string[] args)
{
if (args.Length != 0)
new System.Threading.Thread( new System.Threading.ParameterizedThreadStart(handelexit)).Start(args[0]);
// your code here
}
static void handelexit(object data)
{
int id = System.Convert.ToInt32(data.ToString());
System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(id);
while (!p.HasExited)
System.Threading.Thread.Sleep(100);
System.Environment.Exit(0);
}
}
}

Visual Studio delay between multiple startup projects?

how to add some delay between startup projects in solution?
I want Client project to be started after 2-3 seconds from starting WindowsService.
Why I need this?
WindowsService runs socket server and Client runs socket to connect to server. WindowsService loads slowly than Client, and this causes an exception on client side when connecting to server which is not run yet
I would probably add a retry mechanism within the client. That way not only does it help in the "starting up from Visual Studio" case - it also helps if the server happens to be restarting while the real client connects. The fact that the server is on a faster machine doesn't mean the server will never need to restart, does it?
Indeed, you may well want to add this retry mechanism in such a way that the client can recover even if the server is restarted while it's connected. It depends on what the project is doing, of course.
You can use Mutex locking to sync the two startup project.
Program 1 (StartUp Project 1):
namespace ConsoleApplication1
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
class Program1
{
private static bool isNewMutexCreated = true;
private static Mutex mutex;
static void Main(string[] args)
{
mutex = new Mutex(true, "Global\\ConsoleApplication1", out isNewMutexCreated);
AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
Console.WriteLine("Application1 executed on " + DateTime.Now.ToString());
Console.ReadKey();
}
static void CurrentDomain_ProcessExit(Object sender, EventArgs e)
{
if (isNewMutexCreated)
{
Console.WriteLine("Mutex Released");
mutex.ReleaseMutex();
}
}
}
}
Program 2 (StartUp Project 2):
namespace ConsoleApplication2
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
class Program2
{
static void Main(string[] args)
{
Mutex mutex = null;
Thread.Sleep(5000);
while (mutex == null)
{
try
{
mutex = Mutex.OpenExisting("Global\\ConsoleApplication1");
}
catch (Exception)
{
Console.WriteLine("Mutex not found on " + DateTime.Now.ToString());
Thread.Sleep(3000);
}
}
Console.WriteLine("Application2 executed on " + DateTime.Now.ToString());
Console.ReadKey();
}
}
}
Another simpler option for testing is to just delay the client if the debugger is attached like this:
if (System.Diagnostics.Debugger.IsAttached)
{
System.Threading.Thread.Sleep(2000);
}
You might wrap that in an #if DEBUG block if you like. Anyway I think this should be the least amount of work :)
In case of multiple start-up projects, they are loaded in the order they are specified neither simultaneously nor randomly. http://msdn.microsoft.com/en-us/library/09138bex(v=vs.90).aspx
So ,may be if you specify "client" after "window service", then it may workout fine. And if you don't want to got the coded way suggested above, then (for testing only) you can manually attach the "client" process to you solution from a different solution after your desired delay.
http://msdn.microsoft.com/en-us/library/c6wf8e4z(v=vs.100).aspx
If the client needs to be started after, you need to adjust your list, as at the moment its started before!
I would also code a "/wait" which on loading app if it finds that flag, waits for it maybe useful in use too.
You can set the server project as the single startup project and use this macro to launch the server and the client with a delay:
Sub DebugServerAndClientWithDelay()
DTE.Debugger.Go(False)
System.Threading.Thread.Sleep(2000)
DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate()
DTE.ActiveWindow.Object.GetItem("SolutionName\ClientProjectName").Select(vsUISelectionType.vsUISelectionTypeSelect)
DTE.ExecuteCommand("ClassViewContextMenus.ClassViewProject.Debug.Startnewinstance")
End Sub
You can add a button to your toolbar or use a shortcut key to run this macro.
Just add a procedure to check whether the socket is open or not. If the socket is open continue executing your code and try checking again if the socket is not open. This way even if you start the windows service later there will be no problem.
For the n-tier application I am currently working on, I combined the Mutex method suggested by Romil (slightly different code but same principle) and encapsulated it within a method with a [Conditional("DEBUG")] attribute applied (so it gets stripped out in release mode). We also surround the mutex logic with if (System.Diagnostics.Debugger.IsAttached) {...} since QA builds use Debug mode.
We originally just used a Thread.Sleep with a wait period that worked for most developers machines, but we ran into problems because devs' computer speeds vary and as we added more and more to the server bootstrapper, we had to keep increasing the wait period.
Why don't you just pass an argument to the client application which sets the delay?
static void main(string[] args)
{
// Sleep some time
int delay;
if (args.Length > 0 && int.TryParse(args, out delay))
{
Thread.Sleep(delay);
}
// Initialize client
}
Now you can add the delay in milliseconds to the command-line arguments for the project startup.
I also agree that if possible, it's better to solve your problem structurally, so it doesn't matter when your client and server start.

Find and activate an application's window

Assume that notepad.exe is opening and the it's window is inactive. I will write an application to activate it. How to make?
Update: The window title is undefined. So, I don't like to use to FindWindow which based on window's title.
My application is Winform C# 2.0. Thanks.
You'll need to P/invoke SetForegroundWindow(). Process.MainWindowHandle can give you the handle you'll need. For example:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
class Program {
static void Main(string[] args) {
var prc = Process.GetProcessesByName("notepad");
if (prc.Length > 0) {
SetForegroundWindow(prc[0].MainWindowHandle);
}
}
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
}
Note the ambiguity if you've got more than one copy of Notepad running.
You'd need to PInvoke the Windows API calls such as FindWindow and or EnumWindows and GetWindowText (for the title). Ideally you might also want to use GeWindowThreadProcessId so you can tie it down to the actual process.
You have to use combination of these -
Toggle Process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden at runtime
and
Bring another processes Window to foreground when it has ShowInTaskbar = false
You need to find the class of the window and do a search on it. Read more about it here.
Just for info, Notepad's class name is "Notepad" (without quotes). You can verify it using Spy++.
Note: You cannot activate a window of an app if it was run with no window. Read more options in API here.

How can I run another application within a panel of my C# program?

I've been reading lots on how to trigger an application from inside a C# program (Process.Start()), but I haven t been able to find any information on how to have this new application run within a panel of my C# program. For example, I'd like a button click to open a notepad.exe WITHIN my application, not externally.
Using the win32 API it is possible to "eat" another application. Basically you get the top window for that application and set it's parent to be the handle of the panel you want to place it in. If you don't want the MDI style effect you also have to adjust the window style to make it maximised and remove the title bar.
Here is some simple sample code where I have a form with a button and a panel:
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.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Process p = Process.Start("notepad.exe");
Thread.Sleep(500); // Allow the process to open it's window
SetParent(p.MainWindowHandle, panel1.Handle);
}
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
}
}
I just saw another example where they called WaitForInputIdle instead of sleeping. So the code would be like this:
Process p = Process.Start("notepad.exe");
p.WaitForInputIdle();
SetParent(p.MainWindowHandle, panel1.Handle);
The Code Project has a good article one the whole process: Hosting EXE Applications in a WinForm project
I don't know if this is still the recommended thing to use but the "Object Linking and Embedding" framework allows you to embed certain objects/controls directly into your application. This will probably only work for certain applications, I'm not sure if Notepad is one of them. For really simple things like notepad, you'll probably have an easier time just working with the text box controls provided by whatever medium you're using (e.g. WinForms).
Here's a link to OLE info to get started:
http://en.wikipedia.org/wiki/Object_Linking_and_Embedding
Another interesting solution to luch an exeternal application with a WinForm container is the follow:
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
private void Form1_Load(object sender, EventArgs e)
{
ProcessStartInfo psi = new ProcessStartInfo("notepad.exe");
psi.WindowStyle = ProcessWindowStyle.Minimized;
Process p = Process.Start(psi);
Thread.Sleep(500);
SetParent(p.MainWindowHandle, panel1.Handle);
CenterToScreen();
psi.WindowStyle = ProcessWindowStyle.Normal;
}
The step to ProcessWindowStyle.Minimized from ProcessWindowStyle.Normal remove the annoying delay.
Adding some solution in Answer..**
This code has helped me to dock some executable in windows form. like NotePad, Excel, word, Acrobat reader n many more...
But it wont work for some applications. As sometimes when you start process of some application.... wait for idle time... and the try to get its mainWindowHandle.... till the time the main window handle becomes null.....
so I have done one trick to solve this
If you get main window handle as null... then search all the runnning processes on sytem and find you process ... then get the main hadle of the process and the set panel as its parent.
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "xxxxxxxxxxxx.exe";
info.Arguments = "yyyyyyyyyy";
info.UseShellExecute = true;
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Maximized;
info.RedirectStandardInput = false;
info.RedirectStandardOutput = false;
info.RedirectStandardError = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(info);
p.WaitForInputIdle();
Thread.Sleep(3000);
Process[] p1 ;
if(p.MainWindowHandle == null)
{
List<String> arrString = new List<String>();
foreach (Process p1 in Process.GetProcesses())
{
// Console.WriteLine(p1.MainWindowHandle);
arrString.Add(Convert.ToString(p1.ProcessName));
}
p1 = Process.GetProcessesByName("xxxxxxxxxxxx");
//p.WaitForInputIdle();
Thread.Sleep(5000);
SetParent(p1[0].MainWindowHandle, this.panel2.Handle);
}
else
{
SetParent(p.MainWindowHandle, this.panel2.Handle);
}
I notice that all the prior answers use older Win32 User library functions to accomplish this. I think this will work in most cases, but will work less reliably over time.
Now, not having done this, I can't tell you how well it will work, but I do know that a current Windows technology might be a better solution: the Desktop Windows Manager API.
DWM is the same technology that lets you see live thumbnail previews of apps using the taskbar and task switcher UI. I believe it is closely related to Remote Terminal services.
I think that a probable problem that might happen when you force an app to be a child of a parent window that is not the desktop window is that some application developers will make assumptions about the device context (DC), pointer (mouse) position, screen widths, etc., which may cause erratic or problematic behavior when it is "embedded" in the main window.
I suspect that you can largely eliminate these problems by relying on DWM to help you manage the translations necessary to have an application's windows reliably be presented and interacted with inside another application's container window.
The documentation assumes C++ programming, but I found one person who has produced what he claims is an open source C# wrapper library: https://bytes.com/topic/c-sharp/answers/823547-desktop-window-manager-wrapper. The post is old, and the source is not on a big repository like GitHub, bitbucket, or sourceforge, so I don't know how current it is.
If you want to run notepad inside your app you would probably be better of with a text editor component. There's obviously a basic text box that comes with WinForms, but I suspect more advanced components that offer Notepad functionality (or better) can be found on the interweb.
I know this is possible if the other application can attach itself to a win32 window handle. For example, we have a separate C# application that hosts a DirectX application inside one of its windows. I'm not familiar with the exact details of how this is implemented, but I think just passing the win32 Handle of your panel to the other application is enough for that application to attach its DirectX surface.
Short Answer:
No
Shortish Answer:
Only if the other application is designed to allow it, by providing components for you to add into your own application.

How do I find out if a process is already running using c#?

I have C# winforms application that needs to start an external exe from time to time, but I do not wish to start another process if one is already running, but rather switch to it.
So how in C# would I so this in the example below?
using System.Diagnostics;
...
Process foo = new Process();
foo.StartInfo.FileName = #"C:\bar\foo.exe";
foo.StartInfo.Arguments = "Username Password";
bool isRunning = //TODO: Check to see if process foo.exe is already running
if (isRunning)
{
//TODO: Switch to foo.exe process
}
else
{
foo.Start();
}
This should do it for ya.
Check Processes
//Namespaces we need to use
using System.Diagnostics;
public bool IsProcessOpen(string name)
{
//here we're going to get a list of all running processes on
//the computer
foreach (Process clsProcess in Process.GetProcesses()) {
//now we're going to see if any of the running processes
//match the currently running processes. Be sure to not
//add the .exe to the name you provide, i.e: NOTEPAD,
//not NOTEPAD.EXE or false is always returned even if
//notepad is running.
//Remember, if you have the process running more than once,
//say IE open 4 times the loop thr way it is now will close all 4,
//if you want it to just close the first one it finds
//then add a return; after the Kill
if (clsProcess.ProcessName.Contains(name))
{
//if the process is found to be running then we
//return a true
return true;
}
}
//otherwise we return a false
return false;
}
You can use LINQ as well,
var processExists = Process.GetProcesses().Any(p => p.ProcessName.Contains("<your process name>"));
I have used the AppActivate function in VB runtime to activate an existing process.
You will have to import Microsoft.VisualBasic dll into the C# project.
using System;
using System.Diagnostics;
using Microsoft.VisualBasic;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Process[] proc = Process.GetProcessesByName("notepad");
Interaction.AppActivate(proc[0].MainWindowTitle);
}
}
}
You can simply enumerate processes using Process.GetProcesses method.
I found out that Mutex is not working like in the Console application. So using WMI to query processes that can be seen using Task Manager window will solved your problem.
Use something like this:
static bool isStillRunning() {
string processName = Process.GetCurrentProcess().MainModule.ModuleName;
ManagementObjectSearcher mos = new ManagementObjectSearcher();
mos.Query.QueryString = #"SELECT * FROM Win32_Process WHERE Name = '" + processName + #"'";
if (mos.Get().Count > 1)
{
return true;
}
else
return false;
}
NOTE: Add assembly reference "System.Management" to enable the type intellisense.
I think the complete answer to your problem requires understanding of what happens when your application determines that an instance of foo.exe is already running i.e what does '//TODO: Switch to foo.exe process' actually mean?
In a past project I needed to prevent multiple execution of a process, so I added a some code in the init section of that process which creates a named mutex. This mutext was created and acquired before continuing the rest of the process. If the process can create the mutex and acquire it, then it is the first one running. If another process already controls the mutex, then the one which fails is not the first so it exits immediately.
I was just trying to prevent a second instance from running, due to dependencies on specific hardware interfaces. Depending on what you need with that "switch to" line, you might need a more specific solution such as a process id or handle.
Also, I had source code access to the process I was trying to start. If you can not modify the code, adding the mutex is obviously not an option.
Two concerns to keep in mind:
Your example involved placing a
password on a command line. That
cleartext representation of a secret
could be a security vulnerability.
When enumerating processes, ask
yourself which processes you really
want to enumerate. All users, or
just the current user? What if the
current user is logged in twice (two
desktops)?
Mnebuerquo wrote:
Also, I had source code access to the
process I was trying to start. If you
can not modify the code, adding the
mutex is obviously not an option.
I don't have source code access to the process I want to run.
I have ended up using the proccess MainWindowHandle to switch to the process once I have found it is alread running:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetForegroundWindow(IntPtr hWnd);

Categories

Resources