Hide iTunes window from out of C# program - c#

I want to use the COM interface of iTunes in my C# program. I already imported the type library successfully and tested a bit of code:
using Apple.iTunes;
public partial class MainForm : Form {
private iTunesApp _itapp;
public MainForm() {
InitializeComponent();
_itapp = new iTunesApp();
}
private void Form_Shown(object sender, EventArgs e) {
MessageBox.Show(_itapp.Version);
}
}
That works very well so far. But my problem is that iTunes will be started in background everytime I run my own app.
I know that it's necessary to get access on the COM interface but is there a possibility to let iTunes start with a hidden window or minimize it after starting from within my C# program?
+++ EDIT +++
The FindWindow function from the given MSDN article seems not working anymore. But it was a good reference point and I solved the problem now using the Process class:
public class WindowHandler {
private const int SW_SHOWMINIMIZED = 2;
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
public static void Minimize(string processName) {
Process[] p = Process.GetProcessesByName(processName);
if (p.Length > 0) {
ShowWindowAsync(p[0].MainWindowHandle, SW_SHOWMINIMIZED);
}
}
}
WindowHandler.Minimize("iTunes");
But for the other problem - to start iTunes even in minimized or hidden mode - I still found no way.

Related

SendToBack on startup not working for two or more modeless forms

I create several modeless forms on application startup and try to send them to the background immediately after showing (the forms represent some kind of "yellow notes" the content of which gets deserialized on startup).
For just one form this works as expected, for two or more forms they stay in the foreground. What is wrong?
My demo code:
using System;
using System.Windows.Forms;
namespace TestSendToBack
{
internal sealed class Program
{
[STAThread]
private static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm form1 = new MainForm();
// MainForm form2 = new MainForm(); // comment in to make it fail!
form1.Show();
// form2.Show(); // comment in to make it fail!
Application.Run();
}
}
}
and
using System;
using System.Windows.Forms;
namespace TestSendToBack
{
public class MainForm : Form
{
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
SendToBack();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Application.Exit();
}
}
}
Don't mind the OnClosed, it's only for being able to stop the process. Otherwise it is not part of the problem, of course.
I have found an even better solution to the problem! Instead of sending the forms to the background manually (and keeping them there), I have made the desktop the parent of my forms (see also C# Position Window On Desktop):
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
void SetParentWindowToDesktop()
{
IntPtr hwndThis = this.Handle;
IntPtr hwndParent = FindWindow("ProgMan", null);
SetParent(hwndThis,hwndParent);
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
SetParentWindowToDesktop();
}
This has two additional benefits:
The forms always stay in the background without any further measures
They don't appear in the Alt-Tab-Menu (which I expect from "yellow notes" staying on the desktop)
Since all of this is part of a tray application there is still full control over window creation/destruction and application exit.
Two caveats: Don't use ShowInTaskbar=false because this brings the forms back to the Alt-Tab-Menu (WTF...). SendToBack() makes the forms disappear altogether.

How can I detect when Windows 10 enters tablet mode in a Windows Forms application?

Update
While not the most elegant solution, one method that seems to work is to watch the relevant registry value. Here's an example using WMI to do this. I'd be happy to hear from anyone if there's a better solution than this.
using System;
using System.Management;
using System.Security.Principal;
using System.Windows.Forms;
using Microsoft.Win32;
public partial class MainForm : Form
{
public MainForm()
{
this.InitializeComponent();
this.UpdateModeFromRegistry();
var currentUser = WindowsIdentity.GetCurrent();
if (currentUser != null && currentUser.User != null)
{
var wqlEventQuery = new EventQuery(string.Format(#"SELECT * FROM RegistryValueChangeEvent WHERE Hive='HKEY_USERS' AND KeyPath='{0}\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ImmersiveShell' AND ValueName='TabletMode'", currentUser.User.Value));
var managementEventWatcher = new ManagementEventWatcher(wqlEventQuery);
managementEventWatcher.EventArrived += this.ManagementEventWatcher_EventArrived;
managementEventWatcher.Start();
}
}
private void ManagementEventWatcher_EventArrived(object sender, EventArrivedEventArgs e)
{
this.UpdateModeFromRegistry();
}
private void UpdateModeFromRegistry()
{
var tabletMode = (int)Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ImmersiveShell", "TabletMode", 0);
if (tabletMode == 1)
{
Console.Write(#"Tablet mode is enabled");
}
else
{
Console.Write(#"Tablet mode is disabled");
}
}
}
Original Question
I'm interested in make some optimizations in my Windows Forms application based on whether a user is in "Tablet Mode" (or not) using the new Windows 10 Continuum feature.
There is some guidance on how to do this in a UWP project at https://msdn.microsoft.com/en-us/library/windows/hardware/dn917883(v=vs.85).aspx (i.e. check the current view's UserInteractionMode to see if it's UserInteractionMode.Mouse or UserInteractionMode.Touch), however I'm not sure if or how I can do the same in Windows Forms.
Would there be any way I can call the necessary UWP APIs from my Windows Forms application, or is there some Windows Forms equivalent I can use?
To get whether the system is in tablet mode or not, query the system metric ConvertibleSlateMode like so (not tested, but it should work fine as far back as XP):
public static class TabletPCSupport
{
private static readonly int SM_CONVERTIBLESLATEMODE = 0x2003;
private static readonly int SM_TABLETPC = 0x56;
private static Boolean isTabletPC = false;
public static Boolean SupportsTabletMode { get { return isTabletPC; }}
public static Boolean IsTabletMode
{
get
{
return QueryTabletMode();
}
}
static TabletPCSupport ()
{
isTabletPC = (GetSystemMetrics(SM_TABLETPC) != 0);
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "GetSystemMetrics")]
private static extern int GetSystemMetrics (int nIndex);
private static Boolean QueryTabletMode ()
{
int state = GetSystemMetrics(SM_CONVERTIBLESLATEMODE);
return (state == 0) && isTabletPC;
}
}
(Documentation here)
I have looked everywhere for how to tell if Windows 10 is in tablet mode and here is the simplest solution I found:
bool bIsTabletMode = false;
var uiMode = UIViewSettings.GetForCurrentView().UserInteractionMode;
if (uiMode == Windows.UI.ViewManagement.UserInteractionMode.Touch)
bIsTabletMode = true;
else
bIsTabletMode = false;
// (Could also compare with .Mouse instead of .Touch)
According to this article, you cant listen to WM_SETTINGCHANGE message. Here is a short c# sample :
protected override void WndProc(ref Message m)
{
const int WM_WININICHANGE = 0x001A,
WM_SETTINGCHANGE = WM_WININICHANGE;
if (m.Msg == WM_SETTINGCHANGE)
{
if (Marshal.PtrToStringUni(m.LParam) == "UserInteractionMode")
{
MessageBox.Show(Environment.OSVersion.VersionString);
}
}
base.WndProc(ref m);
}
For Windows 10 you should then perform some COM Interfacing with some WinRT stuff, to check if you are in UserInteractionMode.Mouse (desktop) or UserInteractionMode.Touch (tablet).
The Com Interop stuff looks rather tricky but it seems to be the only way if you are in a stock win32 app.

Changing app.config from another application

I made an application that is able to change its app.config (the connection string part). I tried several solutions and this proved to be the easiest way to solve one of my problems. This is the code I use:
ConnectionStringSettings postavke = new ConnectionStringSettings("Kontrolor.Properties.Settings.KontrolorConnectionString", constring);
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.ConnectionStrings.ConnectionStrings.Clear();
config.ConnectionStrings.ConnectionStrings.Add(postavke);
config.Save(ConfigurationSaveMode.Modified, true);
ConfigurationManager.RefreshSection(config.ConnectionStrings.SectionInformation.SectionName);
This code is placed inside a button_click method, and when I click that button and restart the application the changes are visible.
My question is this - is there a way to do it from another (independent) application that would enable the user to create the connection string by entering the required values into a textBox or selecting it from comboBox (he needs only to enter the IP of the server and the name of the database). By doing that, the first application would be preprepared and there would be no need to restart it to apply changes.
Is there a way to do this?
Since both applications are on same machine you can use simple windows messaging, register windows message in both applications and sender post message to receiver, here is the example code:
Sender :
public partial class FormSender : Form
{
[DllImport("user32")]
private static extern int RegisterWindowMessage(string message);
private static readonly int WM_REFRESH_CONFIGURATION = RegisterWindowMessage("WM_REFRESH_CONFIGURATION");
[DllImport("user32")]
private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
public FormSender()
{
InitializeComponent();
}
private void btnNotify_Click(object sender, EventArgs e)
{
NotifyOtherApp();
}
private void NotifyOtherApp()
{
List<Process> procs = Process.GetProcesses().ToList();
Process receiverProc = procs.Find(pp => pp.ProcessName == "Receiver" || pp.ProcessName == "Receiver.vshost");
if (receiverProc != null)
PostMessage((IntPtr)receiverProc.MainWindowHandle, WM_REFRESH_CONFIGURATION, new IntPtr(0), new IntPtr(0));
}
}
Receiver :
public partial class FormReceiver : Form
{
[DllImport("user32")]
private static extern int RegisterWindowMessage(string message);
private static readonly int WM_REFRESH_CONFIGURATION = RegisterWindowMessage("WM_REFRESH_CONFIGURATION");
public FormReceiver()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_REFRESH_CONFIGURATION)
{
lblMessageReceived.Text = "Refresh message recevied : " + DateTime.Now.ToString();
}
else
{
base.WndProc(ref m);
}
}
}
btw. note that I am checking for process name "Receiver.vshost" so that it can work when started in VS debugger

Pass arguments to running application

I am making an image uploader (upload image to image hosting website) and I'm having some issues passing an argument (image location to an already running application)
First of all let's say MyApp.exe is always running
Whenever I right click on an image I have added an item in the default windows context menu that says "Upload image".
When that's clicked it needs to pass the location to the already running application.
My program.cs:
static class Program
{
[DllImport("user32.dll")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, UIntPtr
wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
[STAThread]
static void Main(params string[] Arguments)
{
if (Arguments.Length > 0)
{
//This means that the the upload item in the context menu is clicked
//Here the method "uploadImage(string location)"
//of the running application must be ran
}
else
{
//just start the application
Application.Run(new ControlPanel());
}
}
}
Note that the ControlPanel class doesn't have a visible form, only a tray icon is present since a form is not needed.
Could I get any help on how to do this?
I have figured it out, so awesome thanks for the person who posted the http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/a5bcfc8a-bf69-4bbc-923d-f30f9ecf5f64 link, this is exactly what I was looking for!
Here's a the full solution:
static class Program
{
[STAThread]
static void Main(params string[] Arguments)
{
SingleInstanceApplication.Run(new ControlPanel(), NewInstanceHandler);
}
public static void NewInstanceHandler(object sender, StartupNextInstanceEventArgs e)
{
string imageLocation = e.CommandLine[1];
MessageBox.Show(imageLocation);
e.BringToForeground = false;
ControlPanel.uploadImage(imageLocation);
}
public class SingleInstanceApplication : WindowsFormsApplicationBase
{
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
{
SingleInstanceApplication app = new SingleInstanceApplication();
app.MainForm = f;
app.StartupNextInstance += startupHandler;
app.Run(Environment.GetCommandLineArgs());
}
}
}
Thanks alot all, and especially the person who posted that link I mentioned above but I guess he deleted his answer?
Regards,
Kenny
Well you will have to establish a communication channel for other applications to post the images to. This communication channel can be one of the following - not a complete list just samples:
A directory that is watched by your app and the file is added once it is added to the directory.
A port where other applications can send information to.
A self-hosted web service that accepts the images.
A TCP port that receives the images.
A named pipe.
....
As you see there are several possibilities. The right one for you depends on your scenario. The file system is an option that can be implemented easily using a FileSystemWatcher for a sample see here.
A self-hosted web sevice exposes a web service that can receive images. See here for a sample.
IMHO, these are the two options that are easiest. But ... there are several more.
For the TCP port see Tim's post.
Assuming that you have control over the execution environment, the listening application could just expose an endpoint using WCF or even a raw TCP socket. That way, any other application can connect to it in a dynamic but structured fashion.
Even though both the sender and receiver are on the same machine, using a network transport solution (like WCF or TCP) is a great way to safely send data across processes.
Here's an example of how to do it in TCP with c#: http://www.switchonthecode.com/tutorials/csharp-tutorial-simple-threaded-tcp-server
WCF can be a bit more complicated (due in part to it's flexibility, and also due to serialization restrictions) but there is plenty of documentation online on how to use it. WCFis a more object-oriented solution because proxy classes can be generated that allow you to make strongly-typed calls to actual objects, versus just sending messages.
To avoid the running of second instance after passing the command line arguments to existing instance, I added below code snippet.
static class Program
{
[STAThread]
static void Main(params string[] Arguments)
{
Form1 MainForm;
bool bInstanceFlag;
Mutex MyApplicationMutex = new Mutex(true, "MyApp_Mutex", out bInstanceFlag);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if (!bInstanceFlag)
{
MainForm = new Form1();
SingleInstanceApplication.Run(MainForm, NewInstanceHandler);
}
else
{
MainForm = new Form1();
SingleInstanceApplication.Run(MainForm, NewInstanceHandler);
MainForm.Close();
}
}
public static void NewInstanceHandler(object sender, StartupNextInstanceEventArgs e)
{
MainForm.AddItem = e.CommandLine[1];
e.BringToForeground = false;
}
public class SingleInstanceApplication : WindowsFormsApplicationBase
{
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
{
SingleInstanceApplication app = new SingleInstanceApplication();
app.MainForm = f;
app.StartupNextInstance += startupHandler;
app.Run(Environment.GetCommandLineArgs());
}
}
}
I added some small additions to the previous solution to reference a setter on the form in order to pass the arguments to it.
So first of all, create a static reference to the initial instance of the form (MainForm).
Then, on subsequent sending of arguments, the NewInstanceHandler can use the saved reference to the form to access it's public methods/properties (in my case, a setter called AddItem).
An easy way to test this out is to add a public property to your form with a setter to change the text property of the form (the title text).
[STAThread]
static Form1 MainForm;
static void Main(params string[] Arguments)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm = new Form1();
SingleInstanceApplication.Run(MainForm, NewInstanceHandler);
}
public static void NewInstanceHandler(object sender, StartupNextInstanceEventArgs e)
{
MainForm.AddItem = e.CommandLine[1];
e.BringToForeground = false;
}
public class SingleInstanceApplication : WindowsFormsApplicationBase
{
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
{
SingleInstanceApplication app = new SingleInstanceApplication();
app.MainForm = f;
app.StartupNextInstance += startupHandler;
app.Run(Environment.GetCommandLineArgs());
}
}

Application exit even not firing in c# windows application

I am using the below class to hide and show the windows task bar.
I am calling the the class like below.
Problem : when i start the application, the taskbar is hiding perfectly.
But when i exit the application by stopping the debugging, the task bar is
not showing up. I mean the application exit is not firing in my code.
I need such a way like, whatever the way i close my application, it shud
finially show the tashbar() before exiting.
Please help. Thanks.
PROGRAM :
static class Program
{
[STAThread]
static void Main()
{
Taskbar.Hide();
Form1 TargerForm = new Form1();
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
Application.EnableVisualStyles();
Application.Run(TargerForm);
}
static void Application_ApplicationExit(object sender, EventArgs e)
{
Taskbar.Show();
}
}
CLASS :
public class Taskbar
{
[DllImport("user32.dll")]
public static extern int FindWindow(string className, string windowText);
[DllImport("user32.dll")]
public static extern int ShowWindow(int hwnd, int command);
public const int SW_HIDE = 0;
public const int SW_SHOW = 1;
public int _taskbarHandle;
protected static int Handle
{
get
{
return FindWindow("Shell_TrayWnd", "");
}
}
public Taskbar()
{
_taskbarHandle = FindWindow("Shell_TrayWnd", "");
}
public static void Show()
{
ShowWindow(Handle, SW_SHOW);
}
public static void Hide()
{
ShowWindow(Handle, SW_HIDE);
}
}
Why not just call Taskbar.Show() after the call to Application.Run? Application.Run will block until the form is closed.
Your code could look like this:
[STAThread]
static void Main()
{
Taskbar.Hide();
Form1 TargerForm = new Form1();
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
Application.EnableVisualStyles();
Application.Run(TargerForm);
Taskbar.Show();
}
When you stop debugging, that's a "rude" exit to a program, and nothing fires after that. MSFT's own Visual Studio integrated web browser suffers from this too. If this is super important to you, you might want to consider running something in the background which takes care of this cleanup for you and doesn't run in the context of the debugger?
I don't think there's a way to capture a "stop debugging" event, but I'd love to be proven wrong :)

Categories