/*
 * Hello Object DLL Self-Registering Server
 * Heavily modified from:
 */
/*
 * SELFREG.CPP
 * Server Self-Registrtation Utility, Chapter 5
 *
 * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
 *
 * Kraig Brockschmidt, Microsoft
 * Internet  :  kraigb@microsoft.com
 * Compuserve:  >INTERNET:kraigb@microsoft.com
 */

import std.c.stdio;
import std.c.stdlib;
import std.c.string;
import std.string;
import std.c.windows.windows;
import std.c.windows.com;

import chello;


//This class factory object creates Hello objects.

class CHelloClassFactory : ComObject, IClassFactory
{
    public:
        this()
	{
	    MessageBoxA(null,"CHelloClassFactory()",null,MB_OK);
	}

        ~this()
	{
	    MessageBoxA(null,"~CHelloClassFactory()",null,MB_OK);
	}

    extern (Windows):

        // IUnknown members
	HRESULT QueryInterface(IID* riid, LPVOID *ppv)
	{
	    MessageBoxA(null,"CHelloClassFactory.QueryInterface()",null,MB_OK);
	    if (IID_IUnknown == *riid)
	    {
		MessageBoxA(null,"IUnknown",null,MB_OK);
		*ppv = cast(void*)cast(IUnknown)this;
	    }
	    else if (IID_IClassFactory == *riid)
	    {
		MessageBoxA(null,"IClassFactory",null,MB_OK);
		*ppv = cast(void*)cast(IClassFactory)this;
	    }
	    else
	    {	*ppv = null;
		return E_NOINTERFACE;
	    }

	    AddRef();
	    return NOERROR;
	}

        // IClassFactory members
	HRESULT CreateInstance(IUnknown pUnkOuter, IID* riid, LPVOID * ppvObj)
	{
	    CHello pObj;
	    HRESULT hr;

	    MessageBoxA(null,"CHelloClassFactory.CreateInstance()",null,MB_OK);
	    *ppvObj = null;
	    hr = E_OUTOFMEMORY;

	    //Verify that a controlling unknown asks for IUnknown
	    if (null !is pUnkOuter && memcmp(&IID_IUnknown, riid, IID.sizeof))
		return CLASS_E_NOAGGREGATION;

	    //Create the object passing function to notify on destruction.
	    pObj = new CHello(pUnkOuter, &ObjectDestroyed);

	    if (!pObj)
		return hr;

	    if (pObj.Init())
	    {
		hr = pObj.QueryInterface(riid, ppvObj);
	    }

	    //Kill the object if initial creation or Init failed.
	    if (FAILED(hr))
		delete pObj;
	    else
		g_cObj++;

	    return hr;
	}


	HRESULT LockServer(BOOL fLock)
	{
	    MessageBoxA(null,"CHelloClassFactory.LockServer()",null,MB_OK);
	    if (fLock)
		g_cLock++;
	    else
		g_cLock--;

	    return NOERROR;
	}

};


//Count number of objects and number of locks.
ULONG       g_cObj=0;
ULONG       g_cLock=0;

HINSTANCE   g_hInst;

extern (C)
{
	void *_atopsp;
	void gc_init();
	void gc_term();
	void _minit();
	void _moduleCtor();
	void _moduleUnitTests();
}

extern (Windows):

BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
{
    //_atopsp = (void*)&hInstance;

    switch (ulReason)
    {
        case DLL_PROCESS_ATTACH:
	    gc_init();
	    _minit();
	    _moduleCtor();
//	    _moduleUnitTests();
	    MessageBoxA(null,"ATTACH",null,MB_OK);
	    break;

        case DLL_PROCESS_DETACH:
	    MessageBoxA(null,"DETACH",null,MB_OK);
	    gc_term();
	    break;

        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
	    // Multiple threads not supported yet
	    MessageBoxA(null,"THREAD",null,MB_OK);
	    return false;
    }

    g_hInst=hInstance;
    return true;
}



/*
 * DllGetClassObject
 *
 * Purpose:
 *  Provides an IClassFactory for a given CLSID that this DLL is
 *  registered to support.  This DLL is placed under the CLSID
 *  in the registration database as the InProcServer.
 *
 * Parameters:
 *  clsID           REFCLSID that identifies the class factory
 *                  desired.  Since this parameter is passed this
 *                  DLL can handle any number of objects simply
 *                  by returning different class factories here
 *                  for different CLSIDs.
 *
 *  riid            REFIID specifying the interface the caller wants
 *                  on the class object, usually IID_ClassFactory.
 *
 *  ppv             LPVOID * in which to return the interface
 *                  pointer.
 *
 * Return Value:
 *  HRESULT         NOERROR on success, otherwise an error code.
 */

HRESULT DllGetClassObject(CLSID* rclsid, IID* riid, LPVOID * ppv)
{
    HRESULT             hr;
    CHelloClassFactory  pObj;

    MessageBoxA(null, "DllGetClassObject()", null, MB_OK);
    //printf("DllGetClassObject()\n");

    if (CLSID_Hello != *rclsid)
        return E_FAIL;

    pObj = new CHelloClassFactory();
    if (!pObj)
        return E_OUTOFMEMORY;

    hr = pObj.QueryInterface(riid, ppv);
    if (FAILED(hr))
        delete pObj;

    return hr;
}





/*
 *  Answers if the DLL can be freed, that is, if there are no
 *  references to anything this DLL provides.
 *
 * Return Value:
 *  BOOL            true if nothing is using us, false otherwise.
 */

HRESULT DllCanUnloadNow()
{
    SCODE   sc;

    MessageBoxA(null, "DllCanUnloadNow()", null, MB_OK);

    // Any locks or objects?
    sc = (0 == g_cObj && 0 == g_cLock) ? S_OK : S_FALSE;
    return sc;
}




/*
 *  Instructs the server to create its own registry entries
 *
 * Return Value:
 *  HRESULT         NOERROR if registration successful, error
 *                  otherwise.
 */

HRESULT DllRegisterServer()
{
    char       szID[128];
    char       szCLSID[128];
    char       szModule[512];

    MessageBoxA(null,"DllRegisterServer()",null,MB_OK);

    //Create some base key strings.
    StringFromGUID2(&CLSID_Hello, cast(LPOLESTR)szID, 128);
    unicode2ansi(szID.ptr);
    strcpy(szCLSID.ptr, "CLSID\\");
    strcat(szCLSID.ptr, szID.ptr);

    //Create ProgID keys
    SetKeyAndValue("Hello1.0", null, "Hello Object");
    SetKeyAndValue("Hello1.0", "CLSID", szID.ptr);

    //Create VersionIndependentProgID keys
    SetKeyAndValue("Hello", null, "Hello Object");
    SetKeyAndValue("Hello", "CurVer", "Hello1.0");
    SetKeyAndValue("Hello", "CLSID", szID.ptr);

    //Create entries under CLSID
    SetKeyAndValue(szCLSID.ptr, null, "Hello Object");
    SetKeyAndValue(szCLSID.ptr, "ProgID", "Hello1.0");
    SetKeyAndValue(szCLSID.ptr, "VersionIndependentProgID", "Hello");
    SetKeyAndValue(szCLSID.ptr, "NotInsertable", null);

    GetModuleFileNameA(g_hInst, szModule.ptr, szModule.length);

    SetKeyAndValue(szCLSID.ptr, "InprocServer32", szModule.ptr);
    return NOERROR;
}



/*
 * Purpose:
 *  Instructs the server to remove its own registry entries
 *
 * Return Value:
 *  HRESULT         NOERROR if registration successful, error
 *                  otherwise.
 */


HRESULT DllUnregisterServer()
{
    char       szID[128];
    char       szCLSID[128];
    char       szTemp[256];

    MessageBoxA(null,"DllUnregisterServer()",null,MB_OK);

    //Create some base key strings.
    StringFromGUID2(&CLSID_Hello, cast(LPOLESTR)szID, 128);
    unicode2ansi(szID.ptr);
    strcpy(szCLSID.ptr, "CLSID\\");
    strcat(szCLSID.ptr, szID.ptr);

    RegDeleteKeyA(HKEY_CLASSES_ROOT, "Hello\\CurVer");
    RegDeleteKeyA(HKEY_CLASSES_ROOT, "Hello\\CLSID");
    RegDeleteKeyA(HKEY_CLASSES_ROOT, "Hello");

    RegDeleteKeyA(HKEY_CLASSES_ROOT, "Hello1.0\\CLSID");
    RegDeleteKeyA(HKEY_CLASSES_ROOT, "Hello1.0");

    strcpy(szTemp.ptr, szCLSID.ptr);
    strcat(szTemp.ptr, "\\");
    strcat(szTemp.ptr, "ProgID");
    RegDeleteKeyA(HKEY_CLASSES_ROOT, szTemp.ptr);

    strcpy(szTemp.ptr, szCLSID.ptr);
    strcat(szTemp.ptr, "\\");
    strcat(szTemp.ptr, "VersionIndependentProgID");
    RegDeleteKeyA(HKEY_CLASSES_ROOT, szTemp.ptr);

    strcpy(szTemp.ptr, szCLSID.ptr);
    strcat(szTemp.ptr, "\\");
    strcat(szTemp.ptr, "NotInsertable");
    RegDeleteKeyA(HKEY_CLASSES_ROOT, szTemp.ptr);

    strcpy(szTemp.ptr, szCLSID.ptr);
    strcat(szTemp.ptr, "\\");
    strcat(szTemp.ptr, "InprocServer32");
    RegDeleteKeyA(HKEY_CLASSES_ROOT, szTemp.ptr);

    RegDeleteKeyA(HKEY_CLASSES_ROOT, szCLSID.ptr);
    return NOERROR;
}



/*
 * SetKeyAndValue
 *
 * Purpose:
 *  Private helper function for DllRegisterServer that creates
 *  a key, sets a value, and closes that key.
 *
 * Parameters:
 *  pszKey          LPTSTR to the name of the key
 *  pszSubkey       LPTSTR ro the name of a subkey
 *  pszValue        LPTSTR to the value to store
 *
 * Return Value:
 *  BOOL            true if successful, false otherwise.
 */

BOOL SetKeyAndValue(LPTSTR pszKey, LPTSTR pszSubkey, LPTSTR pszValue)
{
    HKEY        hKey;
    char       szKey[256];
    BOOL	result;

    strcpy(szKey.ptr, pszKey);

    if (pszSubkey)
    {
        strcat(szKey.ptr, "\\");
        strcat(szKey.ptr, pszSubkey);
    }

    result = true;
    if (ERROR_SUCCESS != RegCreateKeyExA(HKEY_CLASSES_ROOT
        , szKey.ptr, 0, null, REG_OPTION_NON_VOLATILE
        , KEY_ALL_ACCESS, null, &hKey, null))
	result = false;
    else
    {
	if (null!=pszValue)
	{
	    if (RegSetValueExA(hKey, null, 0, REG_SZ, cast(BYTE *)pszValue
		, (strlen(pszValue)+1)*char.sizeof) != ERROR_SUCCESS)
		result = false;
	}

	if (RegCloseKey(hKey) != ERROR_SUCCESS)
	    result = false;
    }
    if (!result)
	MessageBoxA(null,"SetKeyAndValue() failed",null,MB_OK);
    return result;
}




/*
 * ObjectDestroyed
 *
 * Purpose:
 *  Function for the Hello object to call when it gets destroyed.
 *  Since we're in a DLL we only track the number of objects here,
 *  letting DllCanUnloadNow take care of the rest.
 */

extern (D) void ObjectDestroyed()
{
    MessageBoxA(null,"ObjectDestroyed()",null,MB_OK);
    g_cObj--;
}



void unicode2ansi(char *s)
{   wchar *w;

    for (w = cast(wchar *)s; *w; w++)
	*s++ = *w;
    *s = 0;
}

extern (C) int printf(char *format, ...)
{
    return 0;
}

