September 2011

You are browsing the site archives for September 2011.

I spent some more Time improving the App.
It now injects a DLL which listens for the Window Activated Event and then closes it. It also listens for Skype Started Event and tries to close the Window and injects the DLL.

Since DLLs which should be injected can’t be in Managed Code, I had to go back to my C++ knowledge.

Here are the most important parts described:

The DLL
Well, instead of writing too much I’ll just post the complete code for the DLL:

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <iostream>
#include <stdio.h>

HINSTANCE dllInstance;
HHOOK hookHandle;

// Search for the Window and Close it
void CloseWindow() {
	HWND hWnd = FindWindow("THomeForm", "Skype Home");
	if (hWnd != NULL) {
		SendMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
	}
}

// Callback for the Timer
VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) {
	CloseWindow();
	KillTimer(NULL, 1);
}

// Callback for the Hook
LRESULT CALLBACK WindowHookProc(int code, WPARAM wParam, LPARAM lParam)
{
	if (code >= 0) {
		switch(code) {
			//case HCBT_ACTIVATE:
			//case HCBT_CREATEWND:
			//case HSHELL_WINDOWCREATED:
			case HSHELL_WINDOWACTIVATED:
				// Get the Class and the Title
				TCHAR className[255];
				TCHAR title[255];
				memset(className, 0, sizeof(className));
				memset(title, 0, sizeof(title));
				HWND window = (HWND)wParam;
				int ret = GetClassName(window, className, sizeof(className));
				ret = GetWindowText(window, title, sizeof(title));
				// Compare the Class and the Title
				if (strcmp(className, "THomeForm") == 0 && strcmp(title, "Skype Home") == 0) {
					// Success, Create a Timer to close the Window
					int m_LoadTimesTimerID = SetTimer(NULL, 1, 1000, TimerProc);
				}
			break;
		}
	}
	return CallNextHookEx(hookHandle, code, wParam, lParam);
}

extern "C" __declspec(dllexport) void InstallHook(int threadID) {
	int hookID = WH_CBT;
	hookID = WH_SHELL;
	if (threadID > 0) {
		hookHandle = SetWindowsHookEx(hookID, WindowHookProc, dllInstance, threadID);
	} else {
		hookHandle = SetWindowsHookEx(hookID, WindowHookProc, dllInstance, NULL);
	}
}

extern "C" __declspec(dllexport) void UninstallHook() {
	UnhookWindowsHookEx(hookHandle); 
}

BOOL WINAPI DllMain(__in  HINSTANCE hinstDLL, __in  DWORD fdwReason, __in  LPVOID lpvReserved) {
	dllInstance = hinstDLL;
	return TRUE;
}

Calling the unmanaged Code in the Managed C# App
As you see, there are two exported Functions in the DLL:
InstallHook and UninstallHook
To be able to call them, there are some Win32 Stuff and some delegates needed:

[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void InstallHook(int threadID);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void UninstallHook();

Here’s the Code to call the InstallHook:

public static void Install()
{
	IntPtr pDll = LoadLibrary(@"SkypeHomeCloserNativeHelper.dll");
	if (pDll == IntPtr.Zero)
	{
		MessageBox.Show("DLL not found");
	}
	else
	{
		IntPtr procAddress = GetProcAddress(pDll, "InstallHook");
		if (procAddress == IntPtr.Zero)
		{
			MessageBox.Show("Proc not found");
		}
		else
		{
			InstallHook install = (InstallHook)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(InstallHook));
			Process[] procList = Process.GetProcessesByName("Skype");
			if (procList.Length > 0)
			{
				int threadID = procList[0].Threads[0].Id;
				install(threadID);
			}
			bool result = FreeLibrary(pDll);
		}
	}
}

Here’s the Code to uninstall it:

public static void Remove()
{
	IntPtr pDll = LoadLibrary(@"SkypeHomeCloserNativeHelper.dll");
	if (pDll == IntPtr.Zero)
	{
		MessageBox.Show("DLL not found");
	}
	else
	{
		IntPtr procAddress = GetProcAddress(pDll, "UninstallHook");
		if (procAddress == IntPtr.Zero)
		{
			MessageBox.Show("Proc not found");
		}
		else
		{
			UninstallHook uninstall = (UninstallHook)Marshal.GetDelegateForFunctionPointer(procAddress, typeof(UninstallHook));
			uninstall();
			bool result = FreeLibrary(pDll);
		}
	}
}

And here’s the compiled Tool:
SkypeHomeCloser_2.0

Sometimes you might want to listen for a Process Start and do some Actions.

To start listening, just run the following code:

public void StartProcessMonitoring()
{
	// Create event query to be notified within 1 second of a change in a service
	WqlEventQuery query = new WqlEventQuery("__InstanceCreationEvent", new TimeSpan(0, 0, 1), "TargetInstance isa \"Win32_Process\"");

	// Create the Watcher
	watcher = new ManagementEventWatcher();
	watcher.Query = query;
	watcher.EventArrived += new EventArrivedEventHandler(ProcessStartEvent);
	watcher.Start();
}

The callback could then look like this:

public static void ProcessStartEvent(object sender, EventArrivedEventArgs e)
{
	ManagementBaseObject targetInstance = (ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value;
	string processName = targetInstance.Properties["Name"].Value.ToString();
	if (processName == "myprocess.exe")
	{
		// Do whatever you want
	}
}

I created a member for the watcher Object

ManagementEventWatcher watcher;

and closed it when the App closes, otherwise I got a COM Error or something.
Here’s the cleanup Code:

watcher.Stop();
watcher.Dispose();

I hate that Home Screen which pops up on every Skype start. I really do…
So I created a small Tray Icon app which looks for this Window and closes it immediately.

As usual, there is some Win32 API needed to do this. Here’s the Code which searches and closes the Window:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string strClassName, string strWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

public const Int32 WM_SYSCOMMAND = 0x0112;
public const Int32 SC_CLOSE = 0xF060;

public static void Close()
{
	IntPtr hWnd = FindWindow("THomeForm", "Skype Home");
	if (hWnd.ToInt32() > 0)
	{
		SendMessage(hWnd, WM_SYSCOMMAND, new IntPtr(SC_CLOSE), IntPtr.Zero);
	}
}

Here’s the programm: SkypeHomeCloser_1.0

Code printed in Visual Studio 2010 is not colored.
To enable this “feature”, you have to install this Extension.
Be aware that you can’t collapse regions for printing with this Extension.
Furthermore, if you wan’t line numbers, you have to enable them via Tools->Options.