.
Anmeldung | Registrieren | Hilfe

.NET-Blogs Archiv Mai 2013

Specflow kann auch Deutsch

28.05.2013 18:24:09 | Hendrik Loesch

Da Requirements die Grundlage der Entwicklung und somit auch der Kommunikation mit dem Kunden und innerhalb des Teams sind, lohnt es sich in einigen Fällen diese in der Muttersprache des Kunden zu verfassen um Missverständnissen vorzubeugen. Gherkin bzw. Specflow unterstützen dies in dem die Feature Dateien in über 40 unterschiedlichen Sprachen erstellt werden können. Dazu gehört, […]

DNUG Braunschweig Treffen - Windows Phone 8: Unentdeckte Welten

25.05.2013 05:13:00 | Lars Keller

Die DNUG Braunschweig trifft sich am 29.05.2013 um 19:00 im Restaurant Zucker (Tagungsraum). Dieses Mal kommt uns Karsten Samaschke mit einem Vortrag über die Windows Phone 8 Entwicklung besuchen.

Abstract:
Wir schreiben das Jahr 2013, und dies sind die Informationen zu Microsofts Telefonbetriebssystem. Karsten Samaschke spricht in diesem lockeren Talk über Windows Phone 8, wie man damit entwickelt, was es kann und was dabei mal überhaupt nicht geht. Mit euch zusammen erkunden wir die unentdeckten Weiten dieses Systems, sprechen über Features, Geschichte, Optionen und das liebe Geld. Beam me up, Stevie! ??

Karsten Samaschke ist Geschäftsführer der Abteilung Mobil GmbH aus Berlin. Für Microsoft hat er in den letzten Jahren diverse Windows Phone-Veranstaltungen und Entwicklerschulungen durchgeführt. Sollte er einmal Zeit finden, schreibt er immer noch an (s)einem Windows Phone-Buch.

Wie immer ist die Veranstaltung kostenlos! Weitere Informationen zur DNUG Braunschweig können hier gefunden werden: http://www.dotnet-braunschweig.de

Visual Studio Evolution 2013

23.05.2013 17:02:41 | Christian Binder

ich kann schon mal ankündigen, dass wir im Oktober wieder eine Visual Studio Evolution planen. Dort werden wir an einem Tag die aktuellsten Neuerungen zu Visual Studio, .NET und der Windows Plattform zeigen. Wir haben uns schon einiges ausgedacht. Wir halten Euch auf dem laufenden, sobald wir die Details für das Event haben.

Ich selbst werde bis zum 1. September einen verdienten Sabbatical machen und wichtige Dinge in Angriff nehmen Smiley, mein Team wird Euch während dieser Zeit aber weiterhin aktuelle Infos über meinen Blog bereitstellen.

SetWindowHookEx - cool oder nur Fingerübung?

16.05.2013 15:19:00 | Martin Hey

Die Anforderung ist recht schnell erklärt: Erkenne, wann ein Fenster einer Anwendung maximiert, minimiert oder wiederhergestellt wird. "Das kann doch so schwer nicht sein" denkt man sich da, schließlich wirft Windows ja nur so mit Nachrichten um sich, die man einfach nur abfangen muss. Die Windows-API ist dein Freund. Ok, ganz so einfach ist es dann doch nicht, sonst bräuchte dieser Blogpost ja nicht geschrieben werden.

Damit das Fensterhandling und auch so ziemlich alles andere in Windows funktioniert, werden Nachrichten an Fenster gesendet. Zumeist macht Windows das selbst (z.B. Fenster 358: Maximiere dich"). Kommt diese Nachricht am Fenster an, gibt es dort eine Kette von "Abonnenten" - die hook chain. Und in diese Kette kann man eigene Funktionen eingliedern lassen, die dann Teil dieser Kette werden.

Mit diesem Wissen ist die relevante Methode ist auch recht schnell gefunden: SetWindowsHookEx in der user32.dll. Damit gliedert man eine Funktion in die hook chain ein und bekommt dabei einen Handle für den nächsten Teilnehmer der Kette - also den an den die Nachricht dann geschickt werden muss, wenn meine Bearbeitung fertig ist.

Eine gute Anlaufstelle für Fragen, wenn es um die Syntax oder die Verwendung geht, ist entweder die Windows API-Dokumentation in der MSDN - wo C-Syntax verwendet wird und die Parameter inzwischen auch recht ausführlich beschrieben sind - oder pinvoke.net - wo es auch Beispiele zur Umsetzung in C# oder VB.NET gibt. Dort lernt man dann auch schnell, dass man einige Voraussetzungen schaffen muss, um diese Funktionen verwenden zu können.

 

Wie implementiert man den Hook?

Zunächst werden die Methoden definiert, die importiert werden sollen: Mit SetWindowHookEx kann die Methode in die Kette eingereiht werden. Und natürlich benötigt man dann auch noch UnhookWindowsHookEx, um diesen Eintrag später auch wieder entfernen zu können. Des weiteren habe ich schon erwähnt, dass man dafür Sorge tragen muss, den Folgeeintrag in der Kette aufzurufen, deshalb wird auch noch CallNextHookEx benötigt.

[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int code, HookDelegate func, IntPtr hInstance, uint threadId);

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool UnhookWindowsHookEx(IntPtr hhk);

[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

Im Import von SetWindowsHookEx wird im zweiten Parameter auf einen Delegate verwiesen. Dieser muss ebenfalls definiert werden:

public delegate int HookDelegate(int code, IntPtr wParam, IntPtr lParam);

Und man sieht, dass SetWindowsHookEx eine ThreadId erwartet. Damit die Funktion aber nun mit einen Fenster-Handle verwendet werden kann, wird nun noch GetWindowThreadProcessId benötigt, um dieses Mapping durchführen zu können. aber das ist nur Makulatur und für die eigentliche Funktionalität irrelevant:

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);

So weit, so gut. Nun sieht man in den Deklarationen aber auch, dass sehr viele int-Werte verwendet werden. Für Menschen sind Zahlen immer etwas schwierig zu lesen - aus diesem Grund werden noch ein paar Enumerationen deklariert. 

Der Hooktype definiert, welche Art von Nachricht interessant sind. Danach richtet sich im späteren Verlauf, wie die Informationen der Nachricht interpretiert werden müssen:

public enum HookType : int
{
    WH_JOURNALRECORD = 0,
    WH_JOURNALPLAYBACK = 1,
    WH_KEYBOARD = 2,
    WH_GETMESSAGE = 3,
    WH_CALLWNDPROC = 4,
    WH_CBT = 5,
    WH_SYSMSGFILTER = 6,
    WH_MOUSE = 7,
    WH_DEBUG = 9,
    WH_SHELL = 10,
    WH_FOREGROUNDIDLE = 11,
    WH_CALLWNDPROCRET = 12,
}

Für die gewünschte Funktionalität wird WH_CallWndProcRet von Interesse sein, da diese Art von Nachricht dann aufgerufen wird, wenn eine Fensterfunktion fertig ausgeführt ist. Die Nachricht dieses Nachrichtentyps lässt sich mit folgendem Struct darstellen:

[StructLayout(LayoutKind.Sequential)]
public struct CwpRetStruct
{
    public IntPtr lResult;
    public IntPtr lParam;
    public IntPtr wParam;
    public uint message;
    public IntPtr hwnd;
};

Dabei wird dann im Feld message stehen, welche Nachricht geschickt wurde - im gezeigten Fall werden nur Systemcommands von Interesse sein:

public enum WindowsMessages : uint
{
    WM_SYSCOMMAND = 0x0112
}

Und im Feld wParam wird stehen, welche genaue Ausprägung dieses Command hat:

public enum SystemCommands : int
{
    SC_SIZE = 0xF000,
    SC_MOVE = 0xF010,
    SC_MINIMIZE = 0xF020,
    SC_MAXIMIZE = 0xF030,
    SC_NEXTWINDOW = 0xF040,
    SC_PREVWINDOW = 0xF050,
    SC_CLOSE = 0xF060,
    SC_VSCROLL = 0xF070,
    SC_HSCROLL = 0xF080,
    SC_MOUSEMENU = 0xF090,
    SC_KEYMENU = 0xF100,
    SC_ARRANGE = 0xF110,
    SC_RESTORE = 0xF120,
    SC_TASKLIST = 0xF130,
    SC_SCREENSAVE = 0xF140,
    SC_HOTKEY = 0xF150,
    SC_DEFAULT = 0xF160,
    SC_MONITORPOWER = 0xF170,
    SC_CONTEXTHELP = 0xF180,
    SC_SEPARATOR = 0xF00F,

    SCF_ISSECURE = 0x00000001,

    SC_ICON = SC_MINIMIZE,
    SC_ZOOM = SC_MAXIMIZE,
}

Nun aber genug der Vorbereitungen: Die Funktionalität muss auch noch implementiert werden. Der Rumpf meiner Klasse sieht nun wie folgt aus:

public class WindowMessageInterceptor : IDisposable
{
    private IntPtr nextHookPtr;

    private HookDelegate callbackDelegate;

    public WindowMessageInterceptor(IntPtr hWnd)
    {
    }

    public void Dispose()
    {
    }
}

Der Konstruktor erwartet ein Fensterhandle, es gibt Membervariablen für die Delegateinstanz und den Pointer auf den Folgeeintrag in der hook chain, im Konstruktor wird der Hook später gesetzt werden und im Dispose wird der Hook wieder entfernt werden.

Der Konstruktor sieht mit diesem Wissen auch recht unspektakulär aus:

public WindowMessageInterceptor(IntPtr hWnd)
{
    // determine ThreadId for specified Window
    var threadId = GetWindowThreadProcessId(hWnd, IntPtr.Zero);

    // initialize the callback delegate
    this.callbackDelegate = new HookDelegate(this.HookCallbackFunction);

    // setup a window message hook and add it to hook chain
    this.nextHookPtr = SetWindowsHookEx((int)HookType.WH_CALLWNDPROCRET, this.callbackDelegate, IntPtr.Zero, threadId);

    if (this.nextHookPtr == IntPtr.Zero)
    {
        throw new Exception("unable to apply hook");
    }
}

und ebenso das Dispose:

public void Dispose()
{
    if (this.nextHookPtr == IntPtr.Zero)
    {
        return;
    }

    UnhookWindowsHookEx(this.nextHookPtr);
    this.nextHookPtr = IntPtr.Zero;
}

Spannend ist die Callback-Funktion:

private int HookCallbackFunction(int code, IntPtr wParam, IntPtr lParam)
{
    // If code is less than zero, the hook procedure must return the value returned by CallNextHookEx.
    if (code < 0)
    {
        return CallNextHookEx(this.nextHookPtr, code, wParam, lParam);
    }

    // otherwise do all the things and call NextHookEx afterwards
    // convert the lparam to our struct
    var message = (CwpRetStruct)Marshal.PtrToStructure(lParam, typeof(CwpRetStruct));

    // evaluate the message
    if (message.message == (uint)WindowsMessages.WM_SYSCOMMAND)
    {
        Console.Write("syscommand detected: ");

        switch (message.wParam.ToInt32())
        {
            case (int)SystemCommands.SC_RESTORE:
                Debug.WriteLine("window restored from maximized state");
                break;
            case (int)SystemCommands.SC_MINIMIZE:
                Debug.WriteLine("window minimized");
                break;
            case (int)SystemCommands.SC_MAXIMIZE:
                Debug.WriteLine("window maximized");
                break;
            default:
                Debug.WriteLine("not important");
                break;
        }
    }

    return CallNextHookEx(this.nextHookPtr, code, wParam, lParam);
}

Was passiert hier? 

Zunächst wird geprüft, ob der Code größer als 0 ist. Laut API-Dokumentation ist die Nachricht nur dann auch für diese Methode gedacht. Im Anschluss daran muss die Nachricht ausgewertet werden. Damit das passieren kann, bietet das Framework die Methode PtrToStructure. Damit kann der lParam-Wert des Callbacks, in dem sich die Informationen befinden, in das Nachrichtenformat konvertiert werden. Und aus dieser Nachricht interessiert nun, wie man der API entnehmen kann der wParam-Wert.

Im Anschluss wird der Hook an den Folgeempfänger weitergeleitet.

 

Der Test

Für den Test gibt es einen weiteren Konstruktor, der als ThreadId einfach die ThreadId der aktuellen Applikation übergibt:

public WindowMessageInterceptor()
{
    // initialize the callback delegate
    this.callbackDelegate = new HookDelegate(this.HookCallbackFunction);

    // setup a window message hook and add it to hook chain
    this.nextHookPtr = SetWindowsHookEx((int)HookType.WH_CALLWNDPROCRET, this.callbackDelegate, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());

    if (this.nextHookPtr == IntPtr.Zero)
    {
        throw new Exception("unable to apply hook");
    }
}

Und das funktioniert auch ganz gut: Die entsprechenden Nachrichten werden angezeigt. Und auch wenn man das Fensterhandle der aktuellen Anwendung übergibt, funktioniert alles super

Aber.....

Versucht man das Ganze nun mit einem fremden Fensterhandle, so schlägt das Setzen des Hooks fehl. Aber warum?

Auch hier hilft die Dokumentation weiter. Sinngemäß steht dort: Hooks können innerhalb der eigenen Anwendung gesetzt werden, indem als Modul-Handle (Parameter 3) IntPtr.Zero übergeben wird und dann im vierten Parameter die ThreadId. Um den Hook auf fremde Fenster zu setzen, muss als ModulId die ModulId des fremden Programms angegeben werden, der Callback muss in einer Dll sein und das Fremde Programm muss diese Dll geladen haben. Und genau das ist der Knackpunkt, denn hier greifen Sicherheitsmechanismen von .NET, die genau das verhindern.

Resümee

Für den gezeigten Anwendungsfall kommt man mit SetWindowsHookEx nicht weiter, da nur die aktuelle Anwendung abgefragt werden kann. Und das geht mit Bordmitteln wesentlich einfacher, indem man Events auf dem Hauptfenster abonniert - in WPF wäre das das WindowStateChanged-Event.

SetWindowHookEx - cool oder nur Fingerübung?

16.05.2013 15:19:00 | Martin Hey

Die Anforderung ist recht schnell erklärt: Erkenne, wann ein Fenster einer Anwendung maximiert, minimiert oder wiederhergestellt wird. "Das kann doch so schwer nicht sein" denkt man sich da, schließlich wirft Windows ja nur so mit Nachrichten um sich, die man einfach nur abfangen muss. Die Windows-API ist dein Freund. Ok, ganz so einfach ist es dann doch nicht, sonst bräuchte dieser Blogpost ja nicht geschrieben werden.

Damit das Fensterhandling und auch so ziemlich alles andere in Windows funktioniert, werden Nachrichten an Fenster gesendet. Zumeist macht Windows das selbst (z.B. Fenster 358: Maximiere dich"). Kommt diese Nachricht am Fenster an, gibt es dort eine Kette von "Abonnenten" - die hook chain. Und in diese Kette kann man eigene Funktionen eingliedern lassen, die dann Teil dieser Kette werden.

Mit diesem Wissen ist die relevante Methode ist auch recht schnell gefunden: SetWindowsHookEx in der user32.dll. Damit gliedert man eine Funktion in die hook chain ein und bekommt dabei einen Handle für den nächsten Teilnehmer der Kette - also den an den die Nachricht dann geschickt werden muss, wenn meine Bearbeitung fertig ist.

Eine gute Anlaufstelle für Fragen, wenn es um die Syntax oder die Verwendung geht, ist entweder die Windows API-Dokumentation in der MSDN - wo C-Syntax verwendet wird und die Parameter inzwischen auch recht ausführlich beschrieben sind - oder pinvoke.net - wo es auch Beispiele zur Umsetzung in C# oder VB.NET gibt. Dort lernt man dann auch schnell, dass man einige Voraussetzungen schaffen muss, um diese Funktionen verwenden zu können.

 

Wie implementiert man den Hook?

Zunächst werden die Methoden definiert, die importiert werden sollen: Mit SetWindowHookEx kann die Methode in die Kette eingereiht werden. Und natürlich benötigt man dann auch noch UnhookWindowsHookEx, um diesen Eintrag später auch wieder entfernen zu können. Des weiteren habe ich schon erwähnt, dass man dafür Sorge tragen muss, den Folgeeintrag in der Kette aufzurufen, deshalb wird auch noch CallNextHookEx benötigt.

[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int code, HookDelegate func, IntPtr hInstance, uint threadId);

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool UnhookWindowsHookEx(IntPtr hhk);

[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

Im Import von SetWindowsHookEx wird im zweiten Parameter auf einen Delegate verwiesen. Dieser muss ebenfalls definiert werden:

public delegate int HookDelegate(int code, IntPtr wParam, IntPtr lParam);

Und man sieht, dass SetWindowsHookEx eine ThreadId erwartet. Damit die Funktion aber nun mit einen Fenster-Handle verwendet werden kann, wird nun noch GetWindowThreadProcessId benötigt, um dieses Mapping durchführen zu können. aber das ist nur Makulatur und für die eigentliche Funktionalität irrelevant:

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);

So weit, so gut. Nun sieht man in den Deklarationen aber auch, dass sehr viele int-Werte verwendet werden. Für Menschen sind Zahlen immer etwas schwierig zu lesen - aus diesem Grund werden noch ein paar Enumerationen deklariert. 

Der Hooktype definiert, welche Art von Nachricht interessant sind. Danach richtet sich im späteren Verlauf, wie die Informationen der Nachricht interpretiert werden müssen:

public enum HookType : int
{
    WH_JOURNALRECORD = 0,
    WH_JOURNALPLAYBACK = 1,
    WH_KEYBOARD = 2,
    WH_GETMESSAGE = 3,
    WH_CALLWNDPROC = 4,
    WH_CBT = 5,
    WH_SYSMSGFILTER = 6,
    WH_MOUSE = 7,
    WH_DEBUG = 9,
    WH_SHELL = 10,
    WH_FOREGROUNDIDLE = 11,
    WH_CALLWNDPROCRET = 12,
}

Für die gewünschte Funktionalität wird WH_CallWndProcRet von Interesse sein, da diese Art von Nachricht dann aufgerufen wird, wenn eine Fensterfunktion fertig ausgeführt ist. Die Nachricht dieses Nachrichtentyps lässt sich mit folgendem Struct darstellen:

[StructLayout(LayoutKind.Sequential)]
public struct CwpRetStruct
{
    public IntPtr lResult;
    public IntPtr lParam;
    public IntPtr wParam;
    public uint message;
    public IntPtr hwnd;
};

Dabei wird dann im Feld message stehen, welche Nachricht geschickt wurde - im gezeigten Fall werden nur Systemcommands von Interesse sein:

public enum WindowsMessages : uint
{
    WM_SYSCOMMAND = 0x0112
}

Und im Feld wParam wird stehen, welche genaue Ausprägung dieses Command hat:

public enum SystemCommands : int
{
    SC_SIZE = 0xF000,
    SC_MOVE = 0xF010,
    SC_MINIMIZE = 0xF020,
    SC_MAXIMIZE = 0xF030,
    SC_NEXTWINDOW = 0xF040,
    SC_PREVWINDOW = 0xF050,
    SC_CLOSE = 0xF060,
    SC_VSCROLL = 0xF070,
    SC_HSCROLL = 0xF080,
    SC_MOUSEMENU = 0xF090,
    SC_KEYMENU = 0xF100,
    SC_ARRANGE = 0xF110,
    SC_RESTORE = 0xF120,
    SC_TASKLIST = 0xF130,
    SC_SCREENSAVE = 0xF140,
    SC_HOTKEY = 0xF150,
    SC_DEFAULT = 0xF160,
    SC_MONITORPOWER = 0xF170,
    SC_CONTEXTHELP = 0xF180,
    SC_SEPARATOR = 0xF00F,

    SCF_ISSECURE = 0x00000001,

    SC_ICON = SC_MINIMIZE,
    SC_ZOOM = SC_MAXIMIZE,
}

Nun aber genug der Vorbereitungen: Die Funktionalität muss auch noch implementiert werden. Der Rumpf meiner Klasse sieht nun wie folgt aus:

public class WindowMessageInterceptor : IDisposable
{
    private IntPtr nextHookPtr;

    private HookDelegate callbackDelegate;

    public WindowMessageInterceptor(IntPtr hWnd)
    {
    }

    public void Dispose()
    {
    }
}

Der Konstruktor erwartet ein Fensterhandle, es gibt Membervariablen für die Delegateinstanz und den Pointer auf den Folgeeintrag in der hook chain, im Konstruktor wird der Hook später gesetzt werden und im Dispose wird der Hook wieder entfernt werden.

Der Konstruktor sieht mit diesem Wissen auch recht unspektakulär aus:

public WindowMessageInterceptor(IntPtr hWnd)
{
    // determine ThreadId for specified Window
    var threadId = GetWindowThreadProcessId(hWnd, IntPtr.Zero);

    // initialize the callback delegate
    this.callbackDelegate = new HookDelegate(this.HookCallbackFunction);

    // setup a window message hook and add it to hook chain
    this.nextHookPtr = SetWindowsHookEx((int)HookType.WH_CALLWNDPROCRET, this.callbackDelegate, IntPtr.Zero, threadId);

    if (this.nextHookPtr == IntPtr.Zero)
    {
        throw new Exception("unable to apply hook");
    }
}

und ebenso das Dispose:

public void Dispose()
{
    if (this.nextHookPtr == IntPtr.Zero)
    {
        return;
    }

    UnhookWindowsHookEx(this.nextHookPtr);
    this.nextHookPtr = IntPtr.Zero;
}

Spannend ist die Callback-Funktion:

private int HookCallbackFunction(int code, IntPtr wParam, IntPtr lParam)
{
    // If code is less than zero, the hook procedure must return the value returned by CallNextHookEx.
    if (code < 0)
    {
        return CallNextHookEx(this.nextHookPtr, code, wParam, lParam);
    }

    // otherwise do all the things and call NextHookEx afterwards
    // convert the lparam to our struct
    var message = (CwpRetStruct)Marshal.PtrToStructure(lParam, typeof(CwpRetStruct));

    // evaluate the message
    if (message.message == (uint)WindowsMessages.WM_SYSCOMMAND)
    {
        Console.Write("syscommand detected: ");

        switch (message.wParam.ToInt32())
        {
            case (int)SystemCommands.SC_RESTORE:
                Debug.WriteLine("window restored from maximized state");
                break;
            case (int)SystemCommands.SC_MINIMIZE:
                Debug.WriteLine("window minimized");
                break;
            case (int)SystemCommands.SC_MAXIMIZE:
                Debug.WriteLine("window maximized");
                break;
            default:
                Debug.WriteLine("not important");
                break;
        }
    }

    return CallNextHookEx(this.nextHookPtr, code, wParam, lParam);
}

Was passiert hier? 

Zunächst wird geprüft, ob der Code größer als 0 ist. Laut API-Dokumentation ist die Nachricht nur dann auch für diese Methode gedacht. Im Anschluss daran muss die Nachricht ausgewertet werden. Damit das passieren kann, bietet das Framework die Methode PtrToStructure. Damit kann der lParam-Wert des Callbacks, in dem sich die Informationen befinden, in das Nachrichtenformat konvertiert werden. Und aus dieser Nachricht interessiert nun, wie man der API entnehmen kann der wParam-Wert.

Im Anschluss wird der Hook an den Folgeempfänger weitergeleitet.

 

Der Test

Für den Test gibt es einen weiteren Konstruktor, der als ThreadId einfach die ThreadId der aktuellen Applikation übergibt:

public WindowMessageInterceptor()
{
    // initialize the callback delegate
    this.callbackDelegate = new HookDelegate(this.HookCallbackFunction);

    // setup a window message hook and add it to hook chain
    this.nextHookPtr = SetWindowsHookEx((int)HookType.WH_CALLWNDPROCRET, this.callbackDelegate, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());

    if (this.nextHookPtr == IntPtr.Zero)
    {
        throw new Exception("unable to apply hook");
    }
}

Und das funktioniert auch ganz gut: Die entsprechenden Nachrichten werden angezeigt. Und auch wenn man das Fensterhandle der aktuellen Anwendung übergibt, funktioniert alles super

Aber.....

Versucht man das Ganze nun mit einem fremden Fensterhandle, so schlägt das Setzen des Hooks fehl. Aber warum?

Auch hier hilft die Dokumentation weiter. Sinngemäß steht dort: Hooks können innerhalb der eigenen Anwendung gesetzt werden, indem als Modul-Handle (Parameter 3) IntPtr.Zero übergeben wird und dann im vierten Parameter die ThreadId. Um den Hook auf fremde Fenster zu setzen, muss als ModulId die ModulId des fremden Programms angegeben werden, der Callback muss in einer Dll sein und das Fremde Programm muss diese Dll geladen haben. Und genau das ist der Knackpunkt, denn hier greifen Sicherheitsmechanismen von .NET, die genau das verhindern.

Resümee

Für den gezeigten Anwendungsfall kommt man mit SetWindowsHookEx nicht weiter, da nur die aktuelle Anwendung abgefragt werden kann. Und das geht mit Bordmitteln wesentlich einfacher, indem man Events auf dem Hauptfenster abonniert - in WPF wäre das das WindowStateChanged-Event.

All I wanted was a pyramid…

15.05.2013 19:44:19 | Hendrik Loesch

… and all I got was this lousy Christmas tree. I like TDD and I like test automation. It is great to see the application growing with each successful test and to be sure that it will work even if a lot of changes are made on the code base. On the other I see […]

Interessante Trainings zu Visual Studio 2012 in der Microsoft Virtual Academy

15.05.2013 12:30:00 | Christian Binder

Software Testing with Visual Studio 2012 (exam 70-497) Jump Start

Live Event Details
May 28, 2013
9:00am - 5:00pm (PDT)

Link: https://www.microsoftvirtualacademy.com/liveevents/software-testing-with-visual-studio-2012-jump-start?CR_CC=200211930

Administering Visual Studio Team Foundation Server 2012 (exam 70-496) Jump Start

Live Event Details
May 29, 2013
9:00am - 5:00pm (PDT)

Link: https://www.microsoftvirtualacademy.com/liveevents/administering-visual-studio-team-foundation-server-2012-jump-start

Applying ALM with Visual Studio 2012 (exam 70-498) Jump Start

On demand Jump Start training
Link: https://www.microsoftvirtualacademy.com/training-courses/applying-alm-w-visual-studio-2012-jump-start

Building Business Apps with Visual Studio Lightswitch

https://www.microsoftvirtualacademy.com/training-courses/building-business-apps-with-visual-studio-lightswitch

video2brain - Windows Store Apps mit XAML und C# – Das große Training + Geek-Gewinnspiel

13.05.2013 15:10:55 | Gregor Biswanger

Windows_Store_Apps_mit_XAML_und_C#–Das_große_Training

 

Passend zu meiner letzten DVD “Meine erste Windows 8 App”, gibt es jetzt das umfangreiche Video-Training zum Thema “Windows Store Apps mit XAML und C#”. In diesem Video-Training erkunden und erlernen wir ausführlich die faszinierenden Möglichkeiten der App-Entwicklung für Windows 8 und Windows RT. Wir steigen mit den Grundlagen von XAML ein und sind auch bald mit den wichtigsten Neuerungen der Windows Runtime vertraut. Mit den Best Practices zum Thema MVVM - Model View ViewModel - und vielen Profi-Tipps steigst Du bald auf zum versierten App-Entwickler und -Designer.

 

Weitere Informationen

- Produktübersicht
- Inhaltsverzeichnis

 

Kostenfreie Probevideos

 

Einführung in XAML

 

App in den Store stellen

 

Facebook Fanpage

Weitere Tipps und Tricks zur Windows Store App Entwicklung findest du regelmäßig auf der Facebook Fanpage von “Meiner ersten Windows 8 App”: https://www.facebook.com/MeineErsteWindows8App

 

Großes Geek-Gewinnspiel in Kooperation mit WindowsDeveloper.de bis zum 19.05.2013

 

geek-aktion

 

Zu Gewinnen gibt es eine Signierte-Version meiner DVD “Meine erste Windows 8 App”. Dabei wurde die DVD nicht von mir persönlich unterzeichnet, sondern ich habe diese direkt in Redmond vom WinRT-Team verewigen lassen. Dabei sind Unterschriften von Tim Heuer (Program Manager für Microsoft XAML), Unni Ravindranathen (Program Manager für Microsoft Expression) und drei weiteren Mitgliedern des WinRT Teams.

Außerdem verlosen wir vier Zugänge zum neuen video2brain-Online-Training “Windows Store Apps mit XAML und C#”.

 

windows8-gewinnspiel-dvd-feature

 

Zum Gewinnspiel geht es hier lang: [Geek-Gewinnspiel] Zeit für Windows 8! – Einsteiger-Tutorials zu gewinnen

Nun wünsche ich allen viel Spaß beim Rätseln!

 

Besten Dank

Besten Dank an das ganze video2brain-Team!


video2brain_logo

IIS & Windows Authentication – Troubleshooting mit Negotiate & NTLM

12.05.2013 21:50:51 | Robert Mühsig

image.png
Windows Authentifizierung ist eine einfache (und naheliegende) Authentifizierungs-Option für “Haus-interne” Webapplikationen. Setup Im IIS selbst kann man die Windows Authentifzierung sehr leicht anschalten: Natürlich kann man dies auch über die web.config steuern: <system.web> ... <authentication mode="Windows"/> ... </system.web> ... Fehlermeldung “HTTP Error 401.2 – Unauthorized”: Dies kann (wie fast immer) viele Gründe haben, z.B. weil ...

Warum fragmentiert die binäre Serialisierung

03.05.2013 15:49:00 | Klaus Bock

fragmented2... oder; die Krux mit den automatischen Größen von Streams.
Da in der heutigen Zeit, vor allem in der Welt von verwaltetem Code, kaum noch eigene proprietäre Dateiformate Anwendung finden, werden die Allerwenigsten auf dieses Problem stoßen. Auch die Tatsache, dass für beinahe jede Anforderung ein Framework oder eine Bibliothek vorhanden ist, trägt mit Sicherheit dazu bei, solche elementaren Probleme von der Bildfläche verschwinden zu lassen. Für eine ganze Reihe von Entwicklern hört der Gedanke an binäre Serialisierung auf, wenn sie ein Framework wie BSON oder Protocol Buffers zur Anwendung bringen können. Dies soll keine Kritik und schon gar keine negative sein. Ganz im Gegenteil. Diese Frameworks erleichtern die tägliche Arbeit in vielerlei Hinsicht. Verbergen leider aber auch die eigentliche Problematik der Serialisierung. Für mich gehört dieses Thema nach wie vor zu einem der interessantesten in der Softwareentwicklung. Jetzt aber zurück zum eigentlichen Thema.

Für ein aktuelles Projekt verwende ich ein eigenes binäres Dateiformat, in das Datenblöcke in serialisierter und komprimierter Form als byte-Arrays geschrieben werden. Während der Testphase auf einem Livesystem stellte ich fest, dass die Dateigröße überproportional zum Inhalt anwuchs. Zunächst maß ich dem Verhalten keine besondere Bedeutung bei, da ein ähnliches Verhalten auch bei Formaten wie etwa den Access Datenbankdateien oder den Dateien der Windows Registrierung ebenfalls auftritt. Insgesamt ließ mich das “Problem” aber nicht in Ruhe. Nachdem ich mir eine der Dateien im Hex-Format angesehen hatte, war schnell klar, was dem Problem zugrunde lag:
Zwischen den einzelnen Datenblöcken wurde immer eine ganze Menge an 0-Byte-Zeichen geschrieben.

hexview11

Das Resultat war ersichtlich. Aber woher kam dieses Verhalten?
Wie bereits Eingangs erwähnt, werden die Datenblöcke nach dem serialisieren auch komprimiert. Dazu wird die Klasse DeflateStream der Frameworks verwendet. Diese Klasse komprimiert den Inhalt eines Stream in einen anderen. Genau da entstand das Problem.
Das Problem ist vielmehr eine Eigenart der DeflateStream Klasse in Verbindung mit einem MemoryStream. Das Verhalten lässt sich am einfachsten mit einem kleinen Beispiel verdeutlichen. Folgend wird eine Liste aus zufälligen Zeichenfolgen in ein Byte-Array serialisiert und anschließend komprimiert. Mit der Variablen numberOfEntries kann die Anzahl der Einträge in der Liste und dadurch indirekt die Länge des komprimierten Datenblocks gesteuert werden. In der Variablen realLength wird die Anzahl der tatsächlich verwertbaren Bytes im Array ausgegeben. Bei einer geringen Anzahl von Einträgen in der temporären Liste, ist der Wert von realLength immer deutlich kleiner als die Länge des erzeugten Arrays output. Erst ab etwa 130 Einträge in der temporären Liste tritt dieses Verhalten nicht mehr auf. Ab dieser Größe wird die Ausgabe ohne nachgestellte 0-Byte-Zeichen erzeugt.

var numberOfEntries = 10;
var chars = "abcdefghijklmnopqrstuvwxyz";
var tempList = new List<string>(numberOfEntries);
var random = new Random();
for (int i = 0; i < numberOfEntries; i++)
{
    var start = random.Next(0, chars.Length - 1);
    var length = random.Next(start + 1, chars.Length) - start;
    var entry = chars.Substring(start, length);
    tempList.Add(entry);
}
var buffer = Encoding.Default.GetBytes(string.Join("|", tempList));
byte[] output = null;
using (var instream = new MemoryStream(buffer))
{
    using (var outStream = new MemoryStream())
    {
        using (var compress = new DeflateStream(outStream, CompressionMode.Compress))
        {
            instream.CopyTo(compress);
        }
        output = outStream.GetBuffer();
    }
}
// Die Länge des Ausgabe-Arrays
// Unter 130 Einträgen wird fast immer eine Länge von 256 erzeugt.
var arrayLength = output.Length;
// Die tatsächliche Anzahl an verwertbaren Bytes im Array
var realLength = output.Count(b => b > byte.MinValue);

Im obigen Beispiel, wird im zweiten using-Block der MemoryStream für die Ausgabe initialisiert, der nach dem Verlassen des innersten using-Blocks die komprimierten Daten enthält. Initial wird hier ein leerer Stream mit Capacity und Length 0 erzeugt. Der DeflateStream kopiert seinen Inhalt in diesen Stream und legt dabei so lange eine Länge von 256 Bytes fest, bis diese Größe überschritten wird. Erst dann wird der Ausgabestream mit der korrekten Länge des Inhalts des DeflateStream erzeugt. Das bedeutet als Resultat für die, aus den Ergebnissen erzeugte, Datei:

Je mehr Datenblöcke aus Inhalten mit geringer Größe erzeugt werden, desto fragmentierter ist das Ergebnis.

Zur Lösung dieses “Problems”, oder besser unschönen Verhaltens, stehen jetzt zwei Ansatzpunkte zur Auswahl:

  1. Am Anfang des Prozesses, in dem der Konstruktor des Ausgabestreams mit einer kleineren Kapazität initialisiert wird.
  2. Am Ende des Prozesses, in dem die Ausgabe bearbeitet wird.

Den ersten Ansatzpunkt habe ich verworfen, da die Größe des komprimierten Inhalts nicht genau vorhergesagt werden kann. Auch beim Versuch der Initialisierung mit einer Kapazität die klein genug ist, wurde im Test oft genug die Ausgabe mit 256 Bytes im Array erzeugt.

Zur Umsetzung des zweiten Ansatzpunkts, habe ich mich für eine Erweiterungsmethode entschieden, die ähnlich wie die TrimEnd-Methode der String-Klasse, alle nachgestellten 0-Byte-Zeichen aus dem Ausgabe-Array entfernt.

internal static byte[] TrimEnd(this byte[] bytes)
{
    var startLength = bytes.GetLength(0);
    var startPos = startLength - 1;
    var trimPos = startPos;
    for (int i = startPos; i > -1; i--)
    {
        if (bytes[i] > byte.MinValue)
        {
            trimPos = i;
            break;
        }
    }
    if (trimPos == startPos)
    {
        return bytes;
    }
    var trimmedLength = trimPos + 1;
    var trimmed = new byte[trimmedLength];
    Array.Copy(bytes, trimmed, trimmedLength);
    return trimmed;
}

Wenn jetzt beim Abruf des Inhalts diese Erweiterungsmethode aufgerufen wird, ist das Ergebnis jedes mal wie erwartet. Das Problem der Fragmentierung innerhalb der Datei hat sich somit erledigt.

output = outStream.GetBuffer().TrimEnd();

Fazit:

Der bewusste Verzicht auf bestehende Frameworks und Bibliotheken ist oft mit einem, manchmal deutlichem, Mehraufwand verbunden. Besonders dann, wenn unvorhergesehene Probleme oder Eigenarten auftauchen.
Andererseits verschaffen mir gerade diese unvorhersehbaren Situationen einen tieferen Einblick in die jeweilige Materie und somit meist ein besseres Verständnis für das eigentliche Problem.
Wie ich bereits weiter oben gesagt habe: Frameworks für spezielle Anforderungen sind eine feine Sache und meist auch eine große Arbeitserleichterung.
Dennoch wird es immer wieder Entwickler geben die einfach aus Neugier, was ohne sie machbar ist, darauf verzichten.

Technorati-Tags: | | |

Goodbye MDI mit Excel 2013

02.05.2013 12:53:00 | Martin Hey

Ich habe recht häufig den Fall, dass ich zwei Arbeitsblätter nebeneinander liegen haben musste, um diese zu vergleichen. In den letzten Versionen war es es so, dass beim Doppelklick auf eine Datei die mit Excel verknüpft war (*.xlsx, *.csv usw.) diese Datei brav geöffnet wurde, aber dummerweise immer in dem bereits bestehenden Excel-Hauptfenster. Da helfen mir auch die beiden Monitore und der erweiterte Desktop nicht viel. Besonders ärgerlich ist das, da ausnahmslos alle anderen Office-Programme sich hier genau wie erwartet verhielten und ein neues Hauptfenster öffneten.

Vor kurzen habe auch ich den Schritt gewagt und auf Office 2013 aktualisiert. Positiv überrascht war ich, als ich nun wieder genau den "Fehler" machte und einen Doppelklick auf eine Excel-Datei ausführte und mich seelisch schon darauf einstellte, wieder das bekannte Problem zu erleben... aber nein, es öffnete sich ein weiteres Excel-Fenster, welches ich frei positionieren konnte. Kleine Änderung, große Wirkung.

Goodbye MDI mit Excel 2013

02.05.2013 12:53:00 | Martin Hey

Ich habe recht häufig den Fall, dass ich zwei Arbeitsblätter nebeneinander liegen haben musste, um diese zu vergleichen. In den letzten Versionen war es es so, dass beim Doppelklick auf eine Datei die mit Excel verknüpft war (*.xlsx, *.csv usw.) diese Datei brav geöffnet wurde, aber dummerweise immer in dem bereits bestehenden Excel-Hauptfenster. Da helfen mir auch die beiden Monitore und der erweiterte Desktop nicht viel. Besonders ärgerlich ist das, da ausnahmslos alle anderen Office-Programme sich hier genau wie erwartet verhielten und ein neues Hauptfenster öffneten.

Vor kurzen habe auch ich den Schritt gewagt und auf Office 2013 aktualisiert. Positiv überrascht war ich, als ich nun wieder genau den "Fehler" machte und einen Doppelklick auf eine Excel-Datei ausführte und mich seelisch schon darauf einstellte, wieder das bekannte Problem zu erleben... aber nein, es öffnete sich ein weiteres Excel-Fenster, welches ich frei positionieren konnte. Kleine Änderung, große Wirkung.

Mythbuster: 10 Mythen und Irrtuemer ueber Office 365 fuer Privatanwender

02.05.2013 09:32:01 | Kay Giza

Basierend auf meinem Blogposting 'Mythos entzaubert: Microsoft Update, Windows Update, Office Update' hatte ich eigentlich vor, die häufigsten Irrtürmer und Mythen rund um Office 365 hier zu posten. Mein Kollege Stephan Fasshauer hat dies nun wunderbar aufbereitet und dargelegt. Kudos wem Sie gebühren! Sie haben vielleicht auch die gleichen Fragen und brauchen Antworten? Beispielsweise: 'Online-Office' – Office 365 ist nur im Web-Browser verfügbar oder erfordert eine ständige Internetverbindung? Oder: PC-Bindung – Das neue Office ist an meinen PC gefesselt und kann nur einmal installiert werden... [... mehr Informationen in diesem Blogposting auf Giza-Blog.de]

This post is powered by www.Giza-Blog.de | Giza-Blog.de: RSS Feed
© Copyright 2006-2013 Kay Giza. All rights reserved. Legal

Ein Jahr Open Technologies, Inc

01.05.2013 11:54:10 | Mathias Gronau

Vor genau einem Jahr wurde die Microsoft Open Technologies, Inc., kurz MS Open Tech, gegründet (näheres dazu im MSDN-Blog). Die damalige Ausgründung aus dem bestehenden Microsoft Team für Interoperabilitäts-Strategie hat den Open Source Pulsschlag bei Microsoft spürbar erhöht: Es vergeht keine Woche, in der sich Jean Paoli und das MS Open Tech-Team nicht mit einem neuen Open Source-Projekt melden.Denn wenn es darum geht, das Engagement für Offenheit – insbesondere für Interoperabilität, offene Standards und Open Source – deutlicher zu definieren und den Dialog mit der Open Source Community weiter zu vertiefen, kommt es nicht nur auf Handlungsbereitschaft und Ideen an. Es kommt vor allem auch auf das Tempo an. So erlaubt diese Neustrukturierung Microsoft seitdem, quelloffene Software schneller und einfacher zu veröffentlichen, aktiver an bestehenden Open-Source-Projekten mitzuwirken und die Community bei eigenen Projekten stärker einzubinden.Heute, ein Jahr später, blickt Jean Paoli deshalb via Blog Post auf das erste Jahr mit insgesamt 51 Open Source-Projekten zurück. Dazu gehören die Verfügbarkeit des Open Source Android SDK für die Windows Azure Mobile Services, der Windows Azure Plugin für Eclipse mit Java oder der Windows Azure Support für Solr 4.0. Was all diese Projekte gemein haben, ist, dass sie zeigen, welchen Stellenwert der interoperable Ansatz von Windows Azure und offene Schnittstellen für Microsoft heute hat und wie Unternehmen und die Entwickler-Community konkret davon profitieren können.“Offen und flexibel sind bei Windows Azure nicht bloß Attribute”, erklärt hierzu Jan Bach, Geschäftsführer der SYZYGY Deutschland GmbH. “In kürzester Zeit konnten wir eine Linux Ubuntu Distribution für unseren Kunden aufsetzen und je nach Anforderungen skalieren. Als Infrastructure-as-a-Service ermöglicht uns Windows Azure, unseren gesamten LAMP Stack auf Basis von Linux in einer hybriden Cloud zu betreiben und unsere eigenen Managementlösungen für Verwaltung und Monitoring komplexer Unternehmenswebseiten einzubinden.”Wir wünschen Jean Paoli und seinem Team weiterhin viel Erfolg und blicken gespannt auf die nächsten Projekte: Happy Birthday!

Regeln | Impressum