using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
namespace SilentSkype
{
class Program
{
#region Constants
///
/// Tells the window that the other parameter is a system command.
///
private const uint WM_SYSCOMMAND = 0x0112;
///
/// Closes the window.
///
private const uint SC_CLOSE = 0xF060;
#endregion
#region Private Variables
///
/// The executable path to Skype. (The original shortcut had "/nosplash /minimized" on the end.)
///
private static string _exePath = null;
///
/// The caption of the secondary window. This defaults to "Skype Home"
/// but can be specified by the user via the command line.
///
private static string _captionText = "Skype Home";
///
/// The handle to the main Skype window.
///
private static IntPtr _mainWindowHandle = IntPtr.Zero;
///
/// The handle to the infamous "Skype Home" window.
///
private static IntPtr _skypeHomeHandle = IntPtr.Zero;
///
/// The name of the Skype process, to allow checking for it already running.
/// If left as null, the program will not check for an existing instance
/// and will always attempt to launch a new process.
///
private static string _processName = null;
///
/// The maximum length of time to wait for the Skype Home window to appear before giving up.
///
private static TimeSpan _maxWaitTime = new TimeSpan(0, 0, 30);
///
/// The time the process started, so we can ensure we don't spend forever waiting.
/// This defaults to MinValue so that when we subtract it from DateTime.Now,
/// if it hasn't been initiated it'll fail the first time.
///
private static DateTime _startTime = DateTime.MinValue;
///
/// Details of the Skype process we are starting.
///
private static Process _skypeProcess = null;
///
/// The number of ms to wait between trying things.
///
private static int _pauseTime = 500;
#endregion
///
/// The main entrypoint into the application.
/// Everything is done with static variables and methods rather than bothering with
/// creating an instance of something.
///
/// static void Main(string[] args)
{
try
{
if (!ProcessArgs(args))
return; // Something was wrong with the args, so we quit here.
try
{
_startTime = DateTime.Now; // Start timing the overall process, so we don't run too long.
// Start the Skype process.
Console.WriteLine("Checking Skype is running.");
_skypeProcess = StartSkype();
// Ensure the window handles are zero to begin with.
_mainWindowHandle = IntPtr.Zero;
_skypeHomeHandle = IntPtr.Zero;
// Wait for the "Skype Home" window first of all, to avoid getting the handle on the wrong main window.
// The login window appears first of all, and if we're not careful, we can get the handle on that instead
// of the compact view.
while (_skypeHomeHandle == IntPtr.Zero && CanKeepGoing())
{
Console.WriteLine("Waiting for \"Skype Home\" window to be created...");
// Wait a short time.
Thread.Sleep(_pauseTime);
_skypeHomeHandle = FindWindowByCaption(IntPtr.Zero, _captionText);
}
// Update the handle to the main Skype window.
while (_mainWindowHandle == IntPtr.Zero && CanKeepGoing())
{
Console.WriteLine("Waiting for main window to be created...");
// Wait a short time.
Thread.Sleep(_pauseTime);
_skypeProcess.Refresh();
_mainWindowHandle = _skypeProcess.MainWindowHandle;
}
Console.WriteLine("Done getting handles.");
if (_mainWindowHandle != IntPtr.Zero && _skypeHomeHandle != IntPtr.Zero && CanKeepGoing())
{
// Wait for the windows to become visible.
bool mainWindowVisible = false;
bool skypeHomeVisible = false;
do
{
Console.WriteLine("Waiting for windows to become visible.");
// Wait a short time.
Thread.Sleep(_pauseTime);
if (!mainWindowVisible)
mainWindowVisible = IsWindowVisible(_mainWindowHandle);
if (!skypeHomeVisible)
skypeHomeVisible = IsWindowVisible(_skypeHomeHandle);
} while ((!mainWindowVisible || !skypeHomeVisible) && CanKeepGoing());
Console.WriteLine("Closing Skype windows...");
// Close the "Skype Home" window.
if (_skypeHomeHandle != IntPtr.Zero && skypeHomeVisible)
SendMessage(_skypeHomeHandle, WM_SYSCOMMAND, (IntPtr)SC_CLOSE, IntPtr.Zero);
// Close the main window.
if (_mainWindowHandle != IntPtr.Zero && mainWindowVisible)
SendMessage(_mainWindowHandle, WM_SYSCOMMAND, (IntPtr)SC_CLOSE, IntPtr.Zero);
Console.WriteLine("All done.");
}
}
finally
{
// Dispose the process here.
if (_skypeProcess != null)
{
_skypeProcess.Dispose();
_skypeProcess = null;
}
}
}
catch (Exception ex)
{
Console.WriteLine();
Console.WriteLine("Unhandled Exception: " + ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
///
/// This processes the command line arguments and populates variables where relevant.
/// If any problems are detected, the user is notified and validation fails.
///
/// The command line arguments///
private static bool ProcessArgs(string[] args)
{
if (args.Length < 1) { Console.WriteLine("Usage: SilentSkype.exe ExePath [CaptionText] [ProcessName] [MaxWaitTime]"); Console.WriteLine(); Console.WriteLine("ExePath: The path to the executable, including the filename."); Console.WriteLine("CaptionText: The caption of the window to be located."); Console.WriteLine("ProcessName: The name of the process, to check if it's already running."); Console.WriteLine("MaxWaitTime: The maximum length of time (in seconds) that the program will wait for the UI to appear."); return false; } _exePath = args[0]; if (args.Length > 1)
_captionText = args[1];
if (args.Length > 2)
_processName = args[2];
if (args.Length > 3)
{
int maxWaitTime = 0;
if (int.TryParse(args[3], out maxWaitTime))
{
_maxWaitTime = new TimeSpan(0, 0, maxWaitTime);
}
else
{
Console.WriteLine("Could not parse MaxWaitTime as an integer: {0}", args[3]);
return false;
}
}
if (!File.Exists(_exePath))
{
// Return if the executable does not exist.
Console.WriteLine("File not found: {0}", _exePath);
return false;
}
return true;
}
///
/// Checks to see if Skype is already running and start if it's not.
///
///
private static Process StartSkype()
{
Process skypeProcess = null;
if (!String.IsNullOrEmpty(_processName))
{
// Look for a matching process.
Console.WriteLine("Looking for matching process...");
var matchingProcesses = Process.GetProcessesByName(_processName).Where(mp => !mp.HasExited);
if (matchingProcesses.Count() > 0)
{
Console.WriteLine("Skype is already running.");
// Match found - Skype must be running already.
skypeProcess = matchingProcesses.First(); // Take the first instance.
// Dispose of any processes we don't need to keep.
foreach (var item in matchingProcesses)
{
if (item != skypeProcess)
item.Dispose();
}
matchingProcesses = null;
}
else
{
Console.WriteLine("Skype is not running.");
}
}
if (skypeProcess != null)
return skypeProcess;
Console.Write("Launching Skype...");
skypeProcess = Process.Start(_exePath);
Console.WriteLine("done.");
return skypeProcess;
}
///
/// True if we can keep waiting for handles to be created,
/// windows to be visible etc. We should stop if:
/// a) The process has exited. (in case we pick up a closing process by accident)
/// b) We have run out of time
///
///
private static bool CanKeepGoing()
{
if (_skypeProcess.HasExited)
return false; // The process has exited for some reason or other.
else if (DateTime.Now - _startTime >= _maxWaitTime)
return false; // Ran out of time
else
return true; // No other reason to stop.
}
#region Interop
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
#endregion
}
}