//+-------------------------------------------------------------------------
//
//  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 <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;
CLIPFORMAT      g_cfOleClipboardPersistOnFlush= NULL;
CLIPFORMAT      g_cfMoreOlePrivateData = NULL;

ATOM            g_aDropTarget = NULL;
ATOM            g_aDropTargetMarshalHwnd = 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)
//              14-Aug-96 SatishT   Changed the test for notification of Explorer
//                                  to only use the gfShellInitialized flag
//
//  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;
            }

            // initialized DDE only if any server objects have
            // already been registered.
            hr = CheckInitDde(FALSE);
            if (FAILED(hr))
            {
                Assert (!"DDELibMain failed()");
                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.
                // (This is done in file \ntuser\kernel\server.c.)
                // We know they are going to be sequential.  This gives us a
                // good performance improvement (since the clipboard formats never
                // change.

                g_cfObjectLink = (CLIPFORMAT) 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_cfOleClipboardPersistOnFlush = g_cfObjectLink + 19;

                /* turned off till NtUser group checks in for 335613 
                Assert(g_cfOleClipboardPersistOnFlush ==
                    RegisterClipboardFormat(OLESTR("OleClipboardPersistOnFlush")));
                */

                g_cfMoreOlePrivateData = g_cfObjectLink + 20;

                /* turned off till NtUser group checks in for 335613 
                Assert(g_cfMoreOlePrivateData ==
                    RegisterClipboardFormat(OLESTR("MoreOlePrivateData")));
                */

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

                g_aDropTargetMarshalHwnd = GlobalAddAtom(OLE_DROP_TARGET_MARSHALHWND);
                AssertSz(g_aDropTargetMarshalHwnd, "Couldn't add drop target hwnd 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");
                g_aDropTargetMarshalHwnd = GlobalAddAtomA(OLE_DROP_TARGET_MARSHALHWNDA);
                AssertSz(g_aDropTargetMarshalHwnd, "Couldn't add drop target Marshal 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
            CheckUninitDde(FALSE);

            tls->cOleInits--;
            CoUninitialize();
        }
        else
        {
#if defined(_CHICAGO_)
            if (!gfShellInitialized)
            {
                //  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
        CheckUninitDde(FALSE);

        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).
            CheckUninitDde(TRUE);

#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;
}