//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:       ole2.cpp
//
//  Contents:   LibMain and initialization routines
//
//  Classes:
//
//  Functions:  LibMain
//              OleInitialize
//              OleInitializeWOW
//              OleInitializeEx
//              OleUnitialize
//              OleBuildVersion - !WIN32
//
//
//  History:    dd-mmm-yy Author    Comment
//              16-Feb-94 AlexT     alias OleBuildVersion, remove OleGetMalloc
//                                  remove DisableThreadLibaryCalls
//              11-Jan-94 alexgo    added VDATEHEAP macros to every function
//              10-Dec-93 alexgo    added support for LEDebugOut
//              06-Dec-93 ChrisWe   remove declaration of ClipboardInitialize()
//                      and ClipboardUninitialize(), which are declared in
//                      clipbrd.h; include that instead
//              15-Mar-94 KevinRo   Added OleInitializeWOW();
//
//--------------------------------------------------------------------------


#include <le2int.h>
#include <clipbrd.h>
#include <dragopt.h>
#include <drag.h>

#pragma SEG(ole)

#include <olerem.h>
#include <ole2ver.h>
#include <thunkapi.hxx>
#include <ddesrvr.h>
#include <perfmnce.hxx>
#include <olesem.hxx>

//
// DECLARE_INFOLEVEL is a macro used with cairo-style debugging output.
// it creates a global variable LEInfoLevel which contains bits flags
// of the various debugging output that should be sent to the debugger.
//
// Note that info level may be set within the debugger once ole232.dll
// has loaded.
//
// Currently LEInfoLevel defaults to DEB_WARN | DEB_ERROR
//
DECLARE_INFOLEVEL(LE);
DECLARE_INFOLEVEL(Ref);
DECLARE_INFOLEVEL(DD);
DECLARE_INFOLEVEL(VDATE);

NAME_SEG(Ole2Main)
// these are globals

HMODULE         g_hmodOLE2 = NULL;
HINSTANCE	g_hinst = NULL;
ULONG		g_cOleProcessInits = 0;

CLIPFORMAT      g_cfObjectLink = NULL;
CLIPFORMAT      g_cfOwnerLink = NULL;
CLIPFORMAT      g_cfNative = NULL;
CLIPFORMAT      g_cfLink = NULL;
CLIPFORMAT      g_cfBinary = NULL;
CLIPFORMAT      g_cfFileName = NULL;
CLIPFORMAT      g_cfFileNameW = NULL;
CLIPFORMAT      g_cfNetworkName = NULL;
CLIPFORMAT      g_cfDataObject = NULL;
CLIPFORMAT      g_cfEmbeddedObject = NULL;
CLIPFORMAT      g_cfEmbedSource = NULL;
CLIPFORMAT      g_cfCustomLinkSource = NULL;
CLIPFORMAT      g_cfLinkSource = NULL;
CLIPFORMAT      g_cfLinkSrcDescriptor = NULL;
CLIPFORMAT      g_cfObjectDescriptor = NULL;
CLIPFORMAT      g_cfOleDraw = NULL;
CLIPFORMAT      g_cfPBrush = NULL;
CLIPFORMAT      g_cfMSDraw = NULL;
CLIPFORMAT      g_cfOlePrivateData = NULL;
CLIPFORMAT      g_cfScreenPicture = NULL;

ATOM            g_aDropTarget = NULL;

ASSERTDATA

ASSERTOUTDATA

// more globals

extern UINT     uOmPostWmCommand;
extern UINT	uOleMessage;
extern COleStaticMutexSem g_mxsSingleThreadOle;


// this dummy function is used to avoid a copy of the environment variables.
// NOTE: the moniker and dde code still use the windows heap.

extern "C" void _setenvp(void) {
        VDATEHEAP();
 }


#ifdef _CHICAGO_
// Private Chicago Defines
//
//  The Chicago Shell will dynamically load the OLE32.DLL to improve
//  bootup start time.  When an application calls CoInitialize, post
//  a message to the shell to inform it to load OLE32.DLL if it hasn't
//  already.     The Shell will never unload OLE32.DLL.
//
//  We are using an undocumented Shell interface.
//
BOOL    gfShellInitialized = FALSE;

#define WM_SHELLNOTIFY           0x0034
#define SHELLNOTIFY_OLELOADED    0x0002

extern "C" HWND WINAPI GetShellWindow(void);
#endif  // _CHICAGO_

//+---------------------------------------------------------------------------
//
//  Function:   OleInitializeWOW
//  Synopsis:   Entry point to initialize the 16-bit WOW thunk layer.
//
//  Effects:    This routine is called when OLE32 is loaded by a VDM.
//              It serves two functions: It lets OLE know that it is
//              running in a VDM, and it passes in the address to a set
//              of functions that are called by the thunk layer. This
//              allows normal 32-bit processes to avoid loading the WOW
//              DLL since the thunk layer references it.
//
//  Arguments:  [vlpmalloc] -- 16:16 pointer to the 16 bit allocator.
//              [lpthk] -- Flat pointer to the OleThunkWOW virtual
//                         interface. This is NOT an OLE/IUnknown style
//                         interface.
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    3-15-94   kevinro   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
STDAPI OleInitializeWOW( LPMALLOC vlpmalloc, LPOLETHUNKWOW lpthk )
{
    OLETRACEIN((API_OleInitializeWOW, PARAMFMT("vlpmalloc= %x, lpthk= %p"),
                                                                                vlpmalloc, lpthk));

    SetOleThunkWowPtr(lpthk);

    HRESULT hr;

    hr = OleInitializeEx( NULL, COINIT_APARTMENTTHREADED );

    OLETRACEOUT((API_OleInitializeWOW, hr));

    return hr;
}


//+-------------------------------------------------------------------------
//
//  Function:   OleInitialize
//
//  Synopsis:   Initializes OLE in single threaded mode
//
//  Effects:
//
//  Arguments:  [pMalloc]       -- the memory allocator to use
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              06-Dec-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

STDAPI OleInitialize(void * pMalloc)
{
    OLETRACEIN((API_OleInitialize, PARAMFMT("pMalloc= %p"), pMalloc));

    VDATEHEAP();

    HRESULT hr;

    hr = OleInitializeEx( pMalloc, COINIT_APARTMENTTHREADED );

    OLETRACEOUT((API_OleInitialize, hr));

    return hr;
}

//+-------------------------------------------------------------------------
//
//  Function:   OleInitializeEx
//
//  Synopsis:   Initializes ole
//
//  Effects:
//
//  Arguments:  [pMalloc]       -- the task memory allocator to use
//              [flags]         -- single or multi-threaded
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              06-Dec-93 alexgo    32bit port
//              24-May-94 AlexT     Propagate CoInitializeEx's return code
//              21-Jul-94 AlexT     Allow nested OleInit/Uninit calls
//              24-Aug-94 AlexT     Return S_OK for first success and S_FALSE
//                                  thereafter (unless an allocator was
//                                  passed in)
//
//  Notes:      This routine may be called multiple times per apartment
//
//--------------------------------------------------------------------------
#pragma SEG(OleInitialize)
STDAPI OleInitializeEx(LPVOID pMalloc, ULONG ulFlags)
{
    OLETRACEIN((API_OleInitialize, PARAMFMT("pMalloc= %p, ulFlags= %x"), pMalloc, ulFlags));
    VDATEHEAP();

    HRESULT hr;
#if DBG==1
    HRESULT hrCoInit = S_OK;
#endif
    DWORD cThreadOleInits;

    StartPerfCounter(CoInitialize);
    hr = CoInitializeEx(pMalloc, ulFlags);
    EndPerfCounter(CoInitialize);

    if (SUCCEEDED(hr))
    {
	Assert (g_hmodOLE2);
#if DBG==1
	hrCoInit = hr;
#endif

	COleTls tls;
	cThreadOleInits = ++ tls->cOleInits;

	do
	{
	    // We only want to do the below initialization once per apartment
	    if (cThreadOleInits > 1)
	    {
		// We've already been this way before, just return
		Assert(SUCCEEDED(hr) && "Bad OleInitializeEx logic");
		break;
	    }

	    // single thread registration of DDE and clipboard formats.
	    // Only do this once per process.

	    COleStaticLock lck(g_mxsSingleThreadOle);

	    if (++g_cOleProcessInits != 1)
	    {
		// already done the per-process initialization
		break;
	    }

	    if (FALSE==DDELibMain (g_hmodOLE2, (WORD)0, (WORD)0, NULL))
	    {
		Assert (!"DDELibMain failed()");

		hr = E_OUTOFMEMORY;
		break;
	    }

	    // Only need to do the initialization once so check the global
	    // that gets assigned last.

	    if( !g_aDropTarget )
	    {
#ifndef _CHICAGO_
	    // on NT3.51, clipboard formats are pre-registered for us by user32.
	    // We know they are going to be sequential.  This gives us a
	    // good performance improvement (since the clipboard formats never
	    // change.

	    // BUGBUG(alexgo); Chicago needs to get this behaviour too

	    g_cfObjectLink = RegisterClipboardFormat(OLESTR("ObjectLink"));

            g_cfOwnerLink = g_cfObjectLink + 1;
	    Assert(g_cfOwnerLink == RegisterClipboardFormat(OLESTR("OwnerLink")));

            g_cfNative = g_cfObjectLink + 2;
            Assert(g_cfNative == RegisterClipboardFormat(OLESTR("Native")));

            g_cfBinary = g_cfObjectLink + 3;
            Assert(g_cfBinary == RegisterClipboardFormat(OLESTR("Binary")));

            g_cfFileName = g_cfObjectLink + 4;
	    Assert(g_cfFileName == RegisterClipboardFormat(OLESTR("FileName")));

            g_cfFileNameW = g_cfObjectLink + 5;
            Assert(g_cfFileNameW ==
                    RegisterClipboardFormat(OLESTR("FileNameW")));

            g_cfNetworkName = g_cfObjectLink + 6;
            Assert(g_cfNetworkName  ==
                    RegisterClipboardFormat(OLESTR("NetworkName")));

            g_cfDataObject = g_cfObjectLink + 7;
            Assert(g_cfDataObject ==
                    RegisterClipboardFormat(OLESTR("DataObject")));

            g_cfEmbeddedObject = g_cfObjectLink + 8;
            Assert(g_cfEmbeddedObject ==
                    RegisterClipboardFormat(OLESTR("Embedded Object")));

            g_cfEmbedSource = g_cfObjectLink + 9;
            Assert(g_cfEmbedSource ==
                    RegisterClipboardFormat(OLESTR("Embed Source")));

            g_cfCustomLinkSource = g_cfObjectLink + 10;
            Assert(g_cfCustomLinkSource  ==
                    RegisterClipboardFormat(OLESTR("Custom Link Source")));

            g_cfLinkSource = g_cfObjectLink + 11;
            Assert(g_cfLinkSource ==
                    RegisterClipboardFormat(OLESTR("Link Source")));

            g_cfObjectDescriptor = g_cfObjectLink + 12;
            Assert(g_cfObjectDescriptor ==
                    RegisterClipboardFormat(OLESTR("Object Descriptor")));

            g_cfLinkSrcDescriptor = g_cfObjectLink + 13;
            Assert(g_cfLinkSrcDescriptor ==
                    RegisterClipboardFormat(OLESTR("Link Source Descriptor")));

            g_cfOleDraw = g_cfObjectLink + 14;
            Assert(g_cfOleDraw == RegisterClipboardFormat(OLESTR("OleDraw")));

            g_cfPBrush = g_cfObjectLink + 15;
            Assert(g_cfPBrush == RegisterClipboardFormat(OLESTR("PBrush")));

            g_cfMSDraw = g_cfObjectLink + 16;
            Assert(g_cfMSDraw == RegisterClipboardFormat(OLESTR("MSDraw")));

            g_cfOlePrivateData = g_cfObjectLink + 17;
            Assert(g_cfOlePrivateData ==
                    RegisterClipboardFormat(OLESTR("Ole Private Data")));

            g_cfScreenPicture = g_cfObjectLink + 18;
            Assert(g_cfScreenPicture  ==
                RegisterClipboardFormat(OLESTR("Screen Picture")));

            g_aDropTarget = GlobalAddAtom(OLE_DROP_TARGET_PROP);
            AssertSz(g_aDropTarget, "Couldn't add drop target atom\n");
	    }

	    // Used in Inplace editing
	    uOmPostWmCommand = RegisterWindowMessage(OLESTR("OM_POST_WM_COMMAND"));
	    uOleMessage	     = RegisterWindowMessage(OLESTR("OLE_MESSAHE"));

#else  //  !_CHICAGO_

	    g_cfObjectLink	  = SSRegisterClipboardFormatA("ObjectLink");
            g_cfOwnerLink         = SSRegisterClipboardFormatA("OwnerLink");
            g_cfNative            = SSRegisterClipboardFormatA("Native");
            g_cfBinary            = SSRegisterClipboardFormatA("Binary");
            g_cfFileName          = SSRegisterClipboardFormatA("FileName");
            g_cfFileNameW         = SSRegisterClipboardFormatA("FileNameW");
            g_cfNetworkName       = SSRegisterClipboardFormatA("NetworkName");
            g_cfDataObject        = SSRegisterClipboardFormatA("DataObject");
            g_cfEmbeddedObject    = SSRegisterClipboardFormatA("Embedded Object");
            g_cfEmbedSource       = SSRegisterClipboardFormatA("Embed Source");
            g_cfCustomLinkSource  = SSRegisterClipboardFormatA("Custom Link Source");
            g_cfLinkSource        = SSRegisterClipboardFormatA("Link Source");
            g_cfObjectDescriptor  = SSRegisterClipboardFormatA("Object Descriptor");
            g_cfLinkSrcDescriptor = SSRegisterClipboardFormatA("Link Source Descriptor");
            g_cfOleDraw           = SSRegisterClipboardFormatA("OleDraw");
            g_cfPBrush            = SSRegisterClipboardFormatA("PBrush");
            g_cfMSDraw            = SSRegisterClipboardFormatA("MSDraw");
            g_cfOlePrivateData    = SSRegisterClipboardFormatA("Ole Private Data");
            g_cfScreenPicture     = SSRegisterClipboardFormatA("Screen Picture");
            g_aDropTarget         = GlobalAddAtomA(OLE_DROP_TARGET_PROPA);
            AssertSz(g_aDropTarget, "Couldn't add drop target atom\n");
	    }

	    // Used in Inplace editing
	    uOmPostWmCommand = RegisterWindowMessageA("OM_POST_WM_COMMAND");
	    uOleMessage	     = RegisterWindowMessageA("OLE_MESSAHE");

#endif // !_CHICAGO_

	} while (FALSE); // end of do


	if (FAILED(hr))
	{
	    // clean up and break out
	    DDEWEP (WEP_FREE_DLL);

	    tls->cOleInits--;
	    CoUninitialize();
	}
	else
	{
#if defined(_CHICAGO_)
	    if (!gfShellInitialized && CoGetCurrentProcess() == 1)
	    {
		//  The Chicago Shell will dynamically load the OLE32.DLL to improve
		//  bootup start time.	    When an application calls CoInitialize, post
		//  a message to the shell to inform it to load OLE32.DLL if it hasn't
		//  already.   The Shell will never unload OLE32.DLL.
		//
		//  We are using an undocumented Shell interface.
		//
		//  We do this last so that we dont take a task switch while in
		//  CoInitialize.
#if DBG==1
		if (RegQueryValueEx(HKEY_CURRENT_USER,
                            L"Software\\Microsoft\\OLE2\\NoShellNotify",
                            NULL,  // reserved
                            NULL,  // lpdwType
                            NULL,  // lpbData
                            NULL) != ERROR_SUCCESS) // lpcbData
#endif
		{
		    HWND hwndShell = GetShellWindow();
		    if (hwndShell)
		    {
			PostMessage(hwndShell,WM_SHELLNOTIFY,
                                 SHELLNOTIFY_OLELOADED,0L);
		    }
		}

	    }

	    gfShellInitialized = TRUE;
#endif  // _CHICAGO_

	    Assert(SUCCEEDED(hr) && "Bad OleInitializeEx logic");

	    //	If we're overriding the allocator, we return whatever
	    //	CoInitializeEx returned

	    if (NULL != pMalloc)
	    {
		Assert(hr == hrCoInit && "Bad OleInit logic");
	    }
	    else if (1 == cThreadOleInits)
	    {
		//  First successful call to OleInitializeEx - S_OK
		hr = S_OK;
	    }
	    else
	    {
		//  Second or greater succesful call to OleInitializeEx - S_FALSE
		hr = S_FALSE;
	    }
	}
    }

    OLETRACEOUT((API_OleInitialize, hr));
    return hr;
}


//+-------------------------------------------------------------------------
//
//  Function:   OleUnitialize
//
//  Synopsis:   Unitializes OLE, releasing any grabbed resources
//
//  Effects:
//
//  Arguments:  void
//
//  Requires:
//
//  Returns:    void
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              06-Dec-93 alexgo    32bit port
//              21-Jul-94 AlexT     Allow nested OleInit/Uninit calls
//
//  Notes:
//
//--------------------------------------------------------------------------
#pragma SEG(OleUninitialize)
STDAPI_(void) OleUninitialize(void)
{
    OLETRACEIN((API_OleUninitialize, NOPARAM));

    VDATEHEAP();

    COleTls tls(TRUE);

    if (tls.IsNULL() || 0 == tls->cOleInits)
    {
        LEDebugOut((DEB_ERROR,
                    "(0 == thread inits) Unbalanced call to OleUninitialize\n"));
        goto errRtn;
    }

    if (0 == -- tls->cOleInits)
    {
	// This thread has called OleUninitialize for the last time. Check if
	// we need to do per process uninit now.

	ClipboardUninitialize(); // Must be first thing
	DestroyCommonDdeWindow();

	COleStaticLock lck(g_mxsSingleThreadOle);

	if (--g_cOleProcessInits == 0)
	{

	    DragDropProcessUninitialize();

	    // after this point, the uninit should not fail (because we don't
	    // have code to redo the init).
	    DDEWEP (WEP_FREE_DLL);

#if DBG==1
	    // check for unreleased globals
	    UtGlobalFlushTracking();
#endif
	}
    }

    //  We call CoInitialize each time we call OleInitialize, so here we
    //  balance that call
    CoUninitialize();

errRtn:
    OLETRACEOUTEX((API_OleUninitialize, NORETURN));

    return;
}