Click here to Skip to main content
15,896,453 members
Articles / Containers / Virtual Machine

Writing a Parental Control Software for Windows

Rate me:
Please Sign up or sign in to vote.
4.78/5 (9 votes)
8 Jul 2010GPL39 min read 57.2K   2.7K   28  
Writing a parental control software: The beginning of the adventure
//   This file is part of the EasyParentalControl Project.
//
//   EasyParentalControl is free software: you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   EasyParentalControl is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.

//   You should have received a copy of the GNU General Public License
//   along with EasyParentalControl.  If not, see <http://www.gnu.org/licenses/>.

// Parts of this file is derived from the winsock lsp sample, that can be found in the Microsoft Windows SDK v7.0 
#pragma unmanaged

#include "StdAfx.h"

#include "Types.h"
#include "GlobalVars.h"
#include "SockInfo.h"

//
// We must register a window class with a unique name. To do so we'll
// use PROVIDER_CLASS string to format one which includes the current
// process ID. This string will be formatted into a buffer of 
// PROVIDER_CLASS_CHAR_LEN length.
//
#define PROVIDER_CLASS          TEXT("Layered WS2 Provider 0x%08x")
#define PROVIDER_CLASS_CHAR_LEN 32

//
// When handing window messages it is possible we'll get a message for a
// socket not in our list of created sockets. This can occur on an accepted
// socket since some messages may be posted to the accepted socket before
// the SOCK_INFO structure is inserted into the socket list in WSPAccept.
// So if we don't find a SOCK_INFO structure, we'll sleep and try again
// (up to MAX_ASYNC_RETRIES).
//
#define MAX_ASYNC_RETRIES       7
#define ASYNC_TIMEOUT           500     // half a second

#pragma warning(disable:4127)       // Disable conditional expression is constant warning
#pragma warning(disable:4706)       // Disable assignment within conditional warning (message pump)

//
// Function Prototypes
//

// Creates the hidden window and is the message pump for it
static DWORD WINAPI 
AsyncMsgHandler(
    LPVOID  lpParameter
    );

// Handler function for our Winsock FD_* events which posts completions to upper layer
static LRESULT CALLBACK 
AsyncWndProc(
    HWND hwnd, 
    UINT uMsg, 
    WPARAM wParam, 
    LPARAM lParam
    );

//
// Globals to this file
//
static HANDLE WorkerThreadHandle = NULL;        // Dispatch thread for handing window messages
static HWND   AsyncWindow = NULL;               // Handle to the hidden window
static TCHAR  AsyncProviderClassName[ PROVIDER_CLASS_CHAR_LEN ];

//
// Function: StopAsyncWindowManager
//
// Description:
//    This function cleans up the subsystem that handles asynchronous
//    window IO (e.g. WSAAsyncSelect). Basically, it destroys the the
//    hidden window if its been created. Note that this function is
//    only called from WSPCleanup when the DLL is about to be unloaded,
//    and we've already entered the critical section (GlobalVars::gCriticalSection).
//
int 
StopAsyncWindowManager(
    )
{
    int     rc,
            code;

    if ( NULL != AsyncWindow )
    {
        // Post a quit message to the thread
        PostMessage( AsyncWindow, WM_DESTROY, 0, 0 );

        // Wait for the thread to cleanup and exit
        rc = WaitForSingleObject( WorkerThreadHandle, 10000 );
        if ( WAIT_TIMEOUT == rc )
        {
            dbgprint("StopAsyncWindowManager: Timed out waiting for async thread!");
            goto cleanup;
        }

        // Retrieve the exit code and display a simple message
        rc = GetExitCodeThread( WorkerThreadHandle, (LPDWORD) &code );
        if ( 0 == rc )
            dbgprint("StopAsyncWindowManager: Unable to retrieve thread exit code: %d",
                    GetLastError() );
        else if ( 0 != code )
            dbgprint("StopAsyncWindowManager: Async window thread exited abnormally!");

cleanup:

        CloseHandle( WorkerThreadHandle );

        WorkerThreadHandle = NULL;
    }

    return 0;
}

//
// Function: GetWorkerWindow
//
// Description:
//    This returns a handle to our hidden window that acts as an 
//    intermediary between the apps window and winsock. If the window
//    hasn't already been created, then create it.
//
HWND 
GetWorkerWindow(
    )
{
    HANDLE  ReadyEvent = NULL;
    int     rc;

    EnterCriticalSection( &GlobalVars::gCriticalSection );
	if ( NULL == WorkerThreadHandle ) 
    {
        // Create an event which the worker thread will signal when its ready
        ReadyEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
        if ( NULL == ReadyEvent )
        {
            dbgprint("GetWorkerWindow: CreateEvent failed: %d", GetLastError() );
            goto cleanup;
        }

        // Create the asyn window message thread
        WorkerThreadHandle = CreateThread(
                NULL, 
                0, 
                AsyncMsgHandler, 
                (LPVOID)ReadyEvent, 
                0, 
                NULL
                ); 
        if ( NULL == WorkerThreadHandle )
        {
            dbgprint( "GetWorkerWindow: CreateThread failed: %d", GetLastError() );
            goto cleanup;
        }

        // Wait for the window to become initialized
        rc = WaitForSingleObject( ReadyEvent, INFINITE );
        if ( ( WAIT_FAILED == rc ) || ( WAIT_TIMEOUT == rc ) )
            dbgprint( "GetWorkerWindow: WaitForSingleObject failed: %d! (error = %d)", 
                    rc, GetLastError() );
    }

cleanup:

    if ( NULL != ReadyEvent )
    {
        // Close the ready event
        rc = CloseHandle( ReadyEvent );
        if ( 0 == rc )
        {
            dbgprint("GetWorkerWindow: CloseHandle failed: %d", GetLastError() );
        }
    }

    LeaveCriticalSection( &GlobalVars::gCriticalSection );

	return AsyncWindow;
}

//
// Function: AsyncMsgHandler
//
// Description:
//    This is the message pump for our hidden window.
//
static DWORD WINAPI 
AsyncMsgHandler(
    LPVOID lpParameter
    )
{
	MSG      msg;
	DWORD    Ret;
    HANDLE   readyEvent;
    HRESULT  hr;
    WNDCLASS wndclass;


    // Event to signal when window is ready
    readyEvent = (HANDLE) lpParameter;

    hr = StringCchPrintf(
            AsyncProviderClassName,
            PROVIDER_CLASS_CHAR_LEN,
            PROVIDER_CLASS,
            GetCurrentProcessId()
            );
    if ( FAILED( hr ) )
    {
        dbgprint("AsyncMsgHandler: StringCchPrintf failed: %d", GetLastError());
        goto cleanup;
    }

    dbgprint("AsyncMsgHandler: Class name is '%s'", AsyncProviderClassName );

    memset( &wndclass, 0, sizeof( wndclass ) );
    wndclass.lpfnWndProc = (WNDPROC)AsyncWndProc;
    wndclass.hInstance = GlobalVars::gDllInstance;
    wndclass.lpszClassName = AsyncProviderClassName;

    if ( 0 == RegisterClass( &wndclass ) )
    {
        dbgprint("AsyncMsgHandle: RegisterClass failed: %d", GetLastError());
        goto cleanup;
    }

    // Create a window.
    AsyncWindow = CreateWindow(
        AsyncProviderClassName,
        TEXT("Layered Hidden Window"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        GlobalVars::gDllInstance,
        NULL
        );

    if ( NULL == AsyncWindow )
    {
        dbgprint("AsyncMessageHandler: CreateWindow failed: %d", GetLastError() );
        goto cleanup;
    }

    // Indicate the window is ready
    SetEvent( readyEvent );

    // Message pump
	while ( ( Ret = GetMessage( &msg, NULL, 0, 0 ) ) )
	{
		if ( -1 == Ret )
		{
            dbgprint("AsyncMessageHandler: GetMessage returned -1, exiting loop");
            break;
		}
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}

    // Clean up the window and window class which were created in this thread
    if ( NULL != AsyncWindow )
    {
        DestroyWindow( AsyncWindow );
        AsyncWindow = NULL;
    }

    UnregisterClass( AsyncProviderClassName, GlobalVars::gDllInstance );

    ExitThread(0);

cleanup:

    SetEvent( readyEvent );

    ExitThread( (DWORD) -1);
}

//
// Function: AsyncWndProc
//
// Description:
//    This is the window proc for our hidden window. Once we receive a message
//    on our hidden window we must translate our lower provider's socket handle
//    to the app's socket handle and then complete the notification to the user.
//
static LRESULT CALLBACK 
AsyncWndProc(
	HWND    hWnd,
	UINT    uMsg,
	WPARAM  wParam,
	LPARAM  lParam
    )
{
	SOCK_INFO *si = NULL;
    int        retries,
               rc;

	if ( WM_SOCKET == uMsg )
	{
        retries = 0;

        //
        // Given the lower provider's socket handle (the wParam), do a reverse lookup
        // to find the socket context structure. It is possible we received a message
        // for a socket not in our list of sockets. This usually occurs after WSPAccept
        // completes and we immediately get a message for the accepted socket but the
        // LSP code to insert the new context object hasn't executed yet. If this is
        // the case wait on an event until a new context object is added (which 
        // signals the event). Note we'll wait only MAX_ASYNC_RETRIES times before
        // bailing.
        //
        while ( retries < MAX_ASYNC_RETRIES )
        {
            dbgprint("hWnd 0x%p, uMsg 0x%x, WPARAM 0x%p, LPARAM 0x%p (retries %d)",
                    hWnd, uMsg, wParam, lParam, retries);

            // Find the context for this lower provider socket handle
            si = GetCallerSocket( NULL, wParam );
            if ( NULL == si )
            {
                dbgprint("Unable to find socket context for 0x%p", wParam);

                // Wait on an event which is signaled when context is added
                rc = WaitForSingleObject( GlobalVars::gAddContextEvent, ASYNC_TIMEOUT );
                if ( WAIT_FAILED == rc )
                {
                    dbgprint("AsyncWndProc: WaitForSingleObject failed: %d",
                    GetLastError());
                    break;
                }
                else // timeout or success
                {
                    // Reset the event if it was signaled
                    if ( WAIT_OBJECT_0 == rc )
                        ResetEvent( GlobalVars::gAddContextEvent );

                    // If signaled, hopefully next lookup will succeed
                    retries++;
                    continue;
                }
            }

            GlobalVars::gMainUpCallTable.lpWPUPostMessage(
                    si->hWnd, 
                    si->uMsg, 
                    si->LayeredSocket, 
                    lParam
                    );

            return 0;
        }
	}
    else if ( WM_DESTROY == uMsg )
    {
        // Post a quit message to exit our async message pump thread
        PostQuitMessage( 0 );
        return 0;
    }

	return DefWindowProc( hWnd, uMsg, wParam, lParam );
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer (Senior)
France France
I am a French programmer.
These days I spend most of my time with the .NET framework, JavaScript and html.

Comments and Discussions