
/*
 * 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
    override HRESULT QueryInterface(const (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
    override 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;

        default:
            assert(0);
    }

    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(LPCSTR pszKey, LPCSTR pszSubkey, LPCSTR 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++ = cast(char)*w;

    *s = 0;
}

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