//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1993
//
// File: ole2def.c
//
//  Deffered OLE entries.
//
//---------------------------------------------------------------------------

#include "shellprv.h"
#pragma  hdrstop

#define SN_TRACE


typedef HRESULT (STDAPICALLTYPE *LPFNOLEINITIALIZE)(LPMALLOC pMalloc);
typedef void    (STDAPICALLTYPE *LPFNOLEUNINITIALIZE)(void);
typedef HRESULT (STDAPICALLTYPE *LPFNCOGETMALLOC)(DWORD dwMemContext, LPMALLOC *ppMalloc);
typedef HRESULT (STDAPICALLTYPE *LPFNREGISTERDRAGDROP)(HWND hwnd, LPDROPTARGET pDropTarget);
typedef HRESULT (STDAPICALLTYPE *LPFNREVOKEDRAGDROP)(HWND hwnd);
typedef HRESULT (STDAPICALLTYPE *LPFNSTGCREATEDOCFILE)(const OLECHAR *pwcsName,
            DWORD grfMode, DWORD reserved, IStorage **ppstgOpen);
typedef HRESULT (STDAPICALLTYPE *LPFNSTGOPENSTORAGE)(const OLECHAR *pwcsName,
              IStorage *pstgPriority, DWORD grfMode, SNB snbExclude,
              DWORD reserved, IStorage **ppstgOpen);


typedef HRESULT (STDAPICALLTYPE *LPFNOLEQUERYLINKFROMDATA)(LPDATAOBJECT pSrcDataObject);
typedef HRESULT (STDAPICALLTYPE *LPFNOLEQUERYCREATEFROMDATA)(LPDATAOBJECT pSrcDataObject);
typedef HRESULT (STDAPICALLTYPE *LPFNOLEGETCLIPBOARD)(LPDATAOBJECT *ppDataObj);
typedef HRESULT (STDAPICALLTYPE *LPFNOLESETCLIPBOARD)(LPDATAOBJECT pDataObj);
typedef HRESULT (STDAPICALLTYPE *LPFNOLEFLUSHCLIPBOARD)(void);
typedef HRESULT (STDAPICALLTYPE *LPFNDODRAGDROP)(IDataObject *, IDropSource *, DWORD, DWORD *);
typedef HRESULT (STDAPICALLTYPE *LPFNGETCLASSFILE)(const OLECHAR *pwcs, CLSID *pclsid);
// typedef HRESULT (STDAPICALLTYPE *LPFNCREATEFILEMONIKER)(const OLECHAR *pwcs, IMoniker **ppmk);


//
// These need to remain CHAR because they are used with
// GetProcAddress, which is NOT Unicode.
const CHAR c_szRegisterDragDrop[]       = "RegisterDragDrop";
const CHAR c_szRevokeDragDrop[]         = "RevokeDragDrop";
const CHAR c_szOleInitialize[]          = "OleInitialize";
const CHAR c_szOleUnInitialize[]        = "OleUninitialize";
const CHAR c_szStgCreateDocfile[]       = "StgCreateDocfile";
const CHAR c_szStgOpenStorage[]         = "StgOpenStorage";
const CHAR c_szOleQueryLinkFromData[]   = "OleQueryLinkFromData";
const CHAR c_szOleQueryCreateFromData[] = "OleQueryCreateFromData";
const CHAR c_szOleGetClipboard[]        = "OleGetClipboard";
const CHAR c_szOleSetClipboard[]        = "OleSetClipboard";
const CHAR c_szOleFlushClipboard[]      = "OleFlushClipboard";
const CHAR c_szDoDragDrop[]             = "DoDragDrop";
// const char c_szCreateFileMoniker[]   = "CreateFileMoniker";
const CHAR c_szGetClassFile[]           = "GetClassFile";


#pragma data_seg(DATASEG_PERINSTANCE)
HMODULE g_hmodOLE = NULL;
UINT    g_cRefOLE = 0;
BOOL    g_fScmStarted = FALSE;

LPFNREGISTERDRAGDROP       g_pfnRegisterDragDrop = NULL;
LPFNREVOKEDRAGDROP         g_pfnRevokeDragDrop = NULL;
LPFNOLEINITIALIZE          g_pfnOleInitialize = NULL;
LPFNOLEUNINITIALIZE        g_pfnOleUnInitialize = NULL;
LPFNSTGCREATEDOCFILE       g_pfnStgCreateDocfile = NULL;
LPFNSTGOPENSTORAGE         g_pfnStgOpenStorage = NULL;
LPFNOLEQUERYLINKFROMDATA   g_pfnOleQueryLinkFromData = NULL;
LPFNOLEQUERYCREATEFROMDATA g_pfnOleQueryCreateFromData = NULL;
LPFNOLEGETCLIPBOARD        g_pfnOleGetClipboard = NULL;
LPFNOLESETCLIPBOARD        g_pfnOleSetClipboard = NULL;
LPFNOLEFLUSHCLIPBOARD      g_pfnOleFlushClipboard = NULL;
LPFNDODRAGDROP             g_pfnDoDragDrop = NULL;
LPFNGETCLASSFILE           g_pfnGetClassFile = NULL;
// LPFNCREATEFILEMONIKER           g_pfnCreateFileMoniker = NULL;

#pragma data_seg()

//=========================================================================
// SHLoadOLE
// _LoadOLE
// _UnloadOLE
//=========================================================================

STDAPI  _LoadOLE(BOOL fRegisterTargets)
{
    HRESULT hres = NOERROR;

    //
    // Popular "avoid entering critical section" code.
    //
    if (g_hmodOLE==NULL)
    {
        //
        // WARNING: We must not call LoadLibrary or OleInitialize from
        //  within a critical section.
        //  Otherwise, it will cause a dead-lock with SCM.EXE, which loads
        //  SHELL32.DLL.
        //
        HMODULE hmod;
        DebugMsg(DM_TRACE, TEXT("sh TR - Loading OLE"));

        //
        // We must NOT be in the critical section
        //
        ASSERTNONCRITICAL;
        hmod=LoadLibrary(c_szOLE32);

        if (hmod)
        {
            g_pfnRegisterDragDrop = (LPFNREGISTERDRAGDROP)GetProcAddress(hmod, c_szRegisterDragDrop);
            g_pfnRevokeDragDrop   = (LPFNREVOKEDRAGDROP)GetProcAddress(hmod, c_szRevokeDragDrop);
            g_pfnOleInitialize = (LPFNOLEINITIALIZE)GetProcAddress(hmod, c_szOleInitialize);
            g_pfnOleUnInitialize = (LPFNOLEUNINITIALIZE)GetProcAddress(hmod, c_szOleUnInitialize);
            g_pfnStgCreateDocfile = (LPFNSTGCREATEDOCFILE)GetProcAddress(hmod, c_szStgCreateDocfile);
            g_pfnStgOpenStorage = (LPFNSTGOPENSTORAGE)GetProcAddress(hmod, c_szStgOpenStorage);
            g_pfnOleQueryLinkFromData = (LPFNOLEQUERYLINKFROMDATA)GetProcAddress(hmod, c_szOleQueryLinkFromData);
            g_pfnOleQueryCreateFromData = (LPFNOLEQUERYCREATEFROMDATA)GetProcAddress(hmod, c_szOleQueryCreateFromData);
            g_pfnOleGetClipboard = (LPFNOLEGETCLIPBOARD)GetProcAddress(hmod, c_szOleGetClipboard);
            g_pfnOleSetClipboard = (LPFNOLESETCLIPBOARD)GetProcAddress(hmod, c_szOleSetClipboard);
            g_pfnOleFlushClipboard = (LPFNOLEFLUSHCLIPBOARD)GetProcAddress(hmod, c_szOleFlushClipboard);
            g_pfnDoDragDrop = (LPFNDODRAGDROP)GetProcAddress(hmod, c_szDoDragDrop);
            g_pfnGetClassFile = (LPFNGETCLASSFILE)GetProcAddress(hmod, c_szGetClassFile);
            // g_pfnCreateFileMoniker = (LPFNCREATEFILEMONIKER)GetProcAddress(hmod, c_szCreateFileMoniker);

            if (g_pfnRegisterDragDrop==NULL || g_pfnRevokeDragDrop==NULL
                || g_pfnOleInitialize==NULL || g_pfnOleUnInitialize==NULL
                || g_pfnStgCreateDocfile==NULL || g_pfnStgOpenStorage==NULL
                || g_pfnOleQueryLinkFromData==NULL || g_pfnOleQueryCreateFromData==NULL
                || g_pfnOleGetClipboard==NULL || g_pfnOleSetClipboard==NULL
                || g_pfnOleFlushClipboard==NULL
                || g_pfnDoDragDrop==NULL
                || g_pfnGetClassFile == NULL
                // || g_pfnCreateFileMoniker==NULL
                )
            {
                Assert(0);
                hres = E_UNEXPECTED;
            }

            ENTERCRITICAL;
            if (g_hmodOLE==NULL && SUCCEEDED(hres))
            {
                g_hmodOLE = hmod;
                hmod = NULL;
            }
            LEAVECRITICAL;

            //
            // Free the module if not used.
            //
            if (hmod)
            {
                FreeLibrary(hmod);
            }
            else if (fRegisterTargets)
            {
                RegisterShellDropTargetsToOLE();
            }
        }
        else
        {
            // LoadLibrary("OLE32.DLL") failed.
            hres = E_OUTOFMEMORY;
        }
    }

    if (SUCCEEDED(hres)) {
        InterlockedIncrement(&g_cRefOLE);
#ifdef SN_TRACE
        DebugMsg(DM_TRACE, TEXT("sh TR - _LoadOLE g_cRefOle is %d"), g_cRefOLE);
#endif
    }

    return hres;
}

STDAPI  _UnloadOLE()
{
    if (g_cRefOLE==0) {
        Assert(0);
        return E_UNEXPECTED;
    }

    if (InterlockedDecrement(&g_cRefOLE) == 0)
    {
        DebugMsg(DM_TRACE, TEXT("sh TR - Unloading OLE"));
        FreeLibrary(g_hmodOLE);
        g_hmodOLE = NULL;
    }
#ifdef SN_TRACE
    else
    {
        DebugMsg(DM_TRACE, TEXT("sh TR - UnloadOLE g_cRefOle is %d"), g_cRefOLE);
    }
#endif

    return NOERROR;
}

//
// This function must be called only from the shell process.
//
STDAPI SHLoadOLE(LPARAM lParam)
{
    HRESULT hres;
    switch(lParam)
    {
    case SHELLNOTIFY_OLELOADED:
        if (!g_hmodOLE)
        {
            // If we are on a low memory machine, we won't load ole in the
            // shell's context since this seems to slow the shell quite a bit
            // in 4 meg.  But if some shell extension or whomever has loaded
            // ole in our context (GetModuleHandle returns non zero), we will
            // init OLE in the shell.
            if (!((GetSystemMetrics(SM_SLOWMACHINE) & 0x0002) &&
                  !GetModuleHandle(c_szOLE32)))
                hres = _LoadOLE(TRUE);
        }
        break;

    case SHELLNOTIFY_OLEUNLOADED:
        //
        // We never unload OLE from the shell process.
        //
        // hres = _UnloadOLE();
        hres = S_OK;
        break;

    default:
        hres = E_UNEXPECTED;
        break;
    }

    return hres;
}

/* 

 New Code For Ole

 */




#ifdef WINNT

#define SCM_CREATED_EVENT       TEXT("ScmCreatedEvent")
#define SCM_WAIT_MAX            60000

//+---------------------------------------------------------------------------
//
//  Function:   WaitForSCMToInitialize
//
//  Synopsis:   Waits for the OLE SCM process to finish its initialization.
//              This is called before the first call to OleInitialize since
//              the SHELL runs early in the boot process.
//
//  Arguments:  None.
//
//  Returns:    S_OK - SCM is running. OK to call OleInitialize.
//              CO_E_INIT_SCM_EXEC_FAILURE - timed out waiting for SCM
//              other - create event failed
//
//  History:    26-Oct-95   Rickhi  Extracted from CheckAndStartSCM so
//                                  that only the SHELL need call it.
//
//  CODEWORK:   move this code into the SHELL and dont call it from
//              CoInitializeEx.
//
//----------------------------------------------------------------------------
HRESULT WaitForSCMToInitialize()
{
    // create the security attributes needed by CreateEvent

    HANDLE hEvent;
    int rc;

    // Try to create the event - if it already exists the create
    // function still succeeds

    hEvent = CreateEvent(NULL,              // all/anyone access
                         TRUE,              // manual reset
                         FALSE,             // initially not signaled
                         SCM_CREATED_EVENT);// name of the event

    if (hEvent != NULL)
    {
        // wait for the SCM to signal the event, then close the handle
        // and return a code based on the WaitEvent result.

        rc = WaitForSingleObject(hEvent, SCM_WAIT_MAX);

        CloseHandle(hEvent);

        if (rc == WAIT_OBJECT_0)
        {
            g_fScmStarted = TRUE;
            return S_OK;
        }
        else if (rc == WAIT_TIMEOUT)
        {
            return CO_E_INIT_SCM_EXEC_FAILURE;
        }
    }

    // event creation failed or WFSO failed.
    return HRESULT_FROM_WIN32(GetLastError());
}

#endif // WINNT


//=========================================================================
// _LoadAndInitialize
// _UnloadAndUnInitialize
//=========================================================================

HRESULT _LoadAndInitialize()
{
    HRESULT hres;

#ifdef WINNT
    if (!g_fScmStarted)
    {
        hres = WaitForSCMToInitialize();

        // have already verified the SCM is running
        if (FAILED(hres))
            return hres;
    }
#endif

    hres = _LoadOLE(FALSE);
    if (SUCCEEDED(hres)) {
        hres = g_pfnOleInitialize(NULL);
    }

    return hres;
}

void _UnloadAndUnInitialize()
{
    if (g_hmodOLE)
    {
        Assert(g_pfnOleUnInitialize);
        g_pfnOleUnInitialize();
    }
    else
    {
        Assert(0);
    }
    _UnloadOLE();
}

HRESULT _EnsureLoaded()
{
    return g_hmodOLE ? NOERROR : E_UNEXPECTED;
}

//=========================================================================
// OLE API which we load/initialize, uninitialize/unload
//
//  RegisterDragDrop
//  RevokeDragDrop
//
// "SHX" prefix stands for "Shell Fakery OLE functions"
//=========================================================================

HRESULT SHXRegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget)
{
    HRESULT hres = _LoadAndInitialize();

    if (SUCCEEDED(hres))
    {
        hres = g_pfnRegisterDragDrop(hwnd, pDropTarget);
#ifdef SN_TRACE
        DebugMsg(DM_TRACE, TEXT("sh TR - RegisterDragDrop returned (%x)"), hres);
#endif
    }

    return hres;
}



HRESULT SHXRevokeDragDrop(HWND hwnd)
{
    HRESULT hres;

    if (g_hmodOLE)
    {
        Assert(g_pfnRevokeDragDrop);
        hres = g_pfnRevokeDragDrop(hwnd);
#ifdef SN_TRACE
        DebugMsg(DM_TRACE, TEXT("sh TR - RevokeDragDrop returned (%x)"), hres);
#endif
        _UnloadAndUnInitialize();
    }
    else
    {
        Assert(0);
    }

    return hres;
}

//=========================================================================
// OLE API which won't work if OLE is not loaded/initialized.
//
//  _EnsureLoaded
//  StgCreateDocFile
//  StgOpenStorage
//  OleQueryLinkFromData
//  OleQueryCreateFromData
//=========================================================================

HRESULT SHXStgCreateDocfile(const OLECHAR *pwcsName,
            DWORD grfMode,
            DWORD reserved,
            IStorage **ppstgOpen)
{
    HRESULT hres = _EnsureLoaded();
    if (SUCCEEDED(hres))
    {
        Assert(g_pfnStgCreateDocfile);
        hres = g_pfnStgCreateDocfile(pwcsName, grfMode, reserved, ppstgOpen);
        DebugMsg(DM_TRACE, TEXT("sh TR - StgCreateDocfile returned (%x)"), hres);
    }

    return hres;
}


// BUGBUG - Nobody is calling this...!
HRESULT SHXStgOpenStorage(const OLECHAR *pwcsName,
              IStorage *pstgPriority,
              DWORD grfMode,
              SNB snbExclude,
              DWORD reserved,
              IStorage **ppstgOpen)
{
    HRESULT hres = _EnsureLoaded();
    if (SUCCEEDED(hres))
    {
        Assert(g_pfnStgOpenStorage);
        hres = g_pfnStgOpenStorage(pwcsName, pstgPriority, grfMode,
                        snbExclude, reserved, ppstgOpen);
        DebugMsg(DM_TRACE, TEXT("sh TR - StgOpenStorage returned (%x)"), hres);
    }

    return hres;
}



HRESULT SHXOleQueryLinkFromData(IDataObject *pSrcDataObj)
{
    HRESULT hres = _EnsureLoaded();
    if (SUCCEEDED(hres))
    {
        Assert(g_pfnOleQueryLinkFromData);
        hres = g_pfnOleQueryLinkFromData(pSrcDataObj);
        DebugMsg(DM_TRACE, TEXT("sh TR - OleQueryLinkFromData returned (%x)"), hres);
    }

    return hres;
}

HRESULT SHXOleQueryCreateFromData(IDataObject *pSrcDataObj)
{
    HRESULT hres = _EnsureLoaded();
    if (SUCCEEDED(hres))
    {
        Assert(g_pfnOleQueryCreateFromData);
        hres = g_pfnOleQueryCreateFromData(pSrcDataObj);
        DebugMsg(DM_TRACE, TEXT("sh TR - OleQueryCreateFromData returned (%x)"), hres);
    }

    return hres;
}


//=========================================================================
// OLE API which we call real OLE only if OLE is already loaded
//
//  SHDoDragDrop
//  SHSetClipboard
//  OleGetClipboard
//=========================================================================

// this is implemented in oledrag.c for now...
extern BOOL CIDLData_IsSimple(LPDATAOBJECT pdata);

//
// Set this value from the debugger to force OLE drag&drop / clipboard.
//
BOOL g_fUseOle = TRUE;  // enable OLE drag&drop by default!

HRESULT WINAPI SHDoDragDrop(HWND hwndOwner, IDataObject *pdata, IDropSource *pdsrc, DWORD dwEffect, DWORD *pdwEffect)
{
    extern HRESULT ShellDoDragDrop(HWND hwndOwner, IDataObject *pdata, IDropSource *pdsrc, DWORD dwEffect, DWORD *pdwEffect);
    extern HRESULT CDropSource_CreateInstance(IDropSource **ppdsrc);
    extern BOOL g_fDraggingOverSource;
    HRESULT hres;

    IDropSource *pdsrcRelease;

    Assert(g_fDraggingOverSource==FALSE);
    g_fDraggingOverSource = FALSE;      // paranoia

    if (pdsrc == NULL)
    {
        CDropSource_CreateInstance(&pdsrcRelease);
        pdsrc = pdsrcRelease;
    }
    else
        pdsrcRelease = NULL;

    if (g_hmodOLE && (g_fUseOle || !CIDLData_IsSimple(pdata)))
    {
        hres = _EnsureLoaded();
        if (SUCCEEDED(hres))
        {
            Assert(g_pfnDoDragDrop);
            hres = g_pfnDoDragDrop(pdata, pdsrc, dwEffect, pdwEffect);
        }
    }
    else
    {
        hres = ShellDoDragDrop(hwndOwner, pdata, pdsrc, dwEffect, pdwEffect);
    }

    if (pdsrcRelease)
        pdsrcRelease->lpVtbl->Release(pdsrcRelease);

    Assert(g_fDraggingOverSource==FALSE);
    g_fDraggingOverSource = FALSE;      // paranoia

    return hres;
}

STDAPI SHFlushClipboard(void)
{
    HRESULT hres = NOERROR;
    if (g_hmodOLE && g_fUseOle)
    {
        LPDATAOBJECT pdtobj;
        hres = g_pfnOleGetClipboard(&pdtobj);
        if (SUCCEEDED(hres))
        {
            Assert(g_pfnOleFlushClipboard);
            hres = g_pfnOleFlushClipboard();
            pdtobj->lpVtbl->Release(pdtobj);
        }
    }
    return hres;
}

STDAPI SHSetClipboard(IDataObject *pdtobj)
{
    extern HRESULT WINAPI ShellSetClipboard(LPDATAOBJECT pdtobj);
    HRESULT hres = NOERROR;

    if (g_hmodOLE && (g_fUseOle || !CIDLData_IsSimple(pdtobj)))
    {
        hres = _EnsureLoaded();
        if (SUCCEEDED(hres))
        {
            Assert(g_pfnOleSetClipboard);
            hres = g_pfnOleSetClipboard(pdtobj);
            DebugMsg(DM_TRACE, TEXT("sh TR - OleSetClipboard returned (%x)"), hres);
        }
        return hres;
    }

    return ShellSetClipboard(pdtobj);
}

HRESULT SHXOleGetClipboard(IDataObject **ppDataObj)
{
    HRESULT hres;
    if (g_hmodOLE)
    {
        hres = _EnsureLoaded();
        if (SUCCEEDED(hres))
        {
            Assert(g_pfnOleGetClipboard);
            hres = g_pfnOleGetClipboard(ppDataObj);
            DebugMsg(DM_TRACE, TEXT("sh TR - OleGetClipboard returned (%x, %x)"), hres, *ppDataObj);
        }
    }
    else
    {
        hres = E_UNEXPECTED;
    }

    return hres;
}

HRESULT SHXGetClassFile(const OLECHAR *pwcs, CLSID *pclsid)
{
    HRESULT hres = _EnsureLoaded();
    if (SUCCEEDED(hres))
    {
        Assert(g_pfnGetClassFile);
        hres = g_pfnGetClassFile(pwcs, pclsid);
        DebugMsg(DM_TRACE, TEXT("sh TR - GetClassFile returned (%x)"), hres);
    }

    return hres;
}

#if 0
HRESULT CreateFileMoniker(const OLECHAR *pwcs, IMoniker **ppmk)
{
    HRESULT hres = _EnsureLoaded();
    if (SUCCEEDED(hres))
    {
        Assert(g_pfnCreateFileMoniker);
        hres = g_pfnCreateFileMoniker(pwcs, ppmk);
        DebugMsg(DM_TRACE, TEXT("sh TR - CreateFileMoniker returned (%x)"), hres);
    }

    return hres;
}
#endif