// cntrdll.cpp : Contains CNTRDLL.DLL implementation and initialization
//              code.

#include "stdafx.h"
#include "cntrdll.h" 

#include "cntritem.h"
#include "memory.h"

/////////////////////////////////////////////////////////////////////////////


class CClientItem : public CCmdTarget {
public:
	CClientItem();
	~CClientItem();
	void LoadEmbedding(void _huge* pData, long nSize);
	void LoadEmbedding2(HGLOBAL hGlobal);
	LPOLECLIENTSITE GetClientSite();
	LPOLEOBJECT m_lpObject;

// Interface Maps
protected:
	BEGIN_INTERFACE_PART(OleClientSite, IOleClientSite)
		STDMETHOD(SaveObject)();
		STDMETHOD(GetMoniker)(DWORD, DWORD, LPMONIKER FAR*);
		STDMETHOD(GetContainer)(LPOLECONTAINER FAR*);
		STDMETHOD(ShowObject)();
		STDMETHOD(OnShowWindow)(BOOL);
		STDMETHOD(RequestNewObjectLayout)();
	END_INTERFACE_PART(OleClientSite) 
	
	DECLARE_INTERFACE_MAP()
};

typedef void** LPLP;

/////////////////////////////////////////////////////////////////////////////
// Public C interface

extern "C" void* FAR PASCAL _export CntrDLLInsertObjectNew()
{
	COleDocument* pOleDocument;
	// Invoke the standard Insert Object dialog box to obtain information
	//  for new CCntrItem object.
	COleInsertDialog dlg;
	if (dlg.DoModal() != IDOK)
		return NULL;

	CCntrItem* pItem = NULL;
	TRY
	{
		// Create new item connected to this document.
		COleDocument* pDoc = new COleDocument; 
		pOleDocument = pDoc;
		ASSERT_VALID(pDoc);
		pItem = new CCntrItem(pDoc);
		ASSERT_VALID(pItem);

		// Initialize the item from the dialog data.
		if (!dlg.CreateItem(pItem))
			AfxThrowMemoryException();  // any exception will do
		ASSERT_VALID(pItem);

		pItem->UpdateLink();
		pItem->UpdateFromServerExtent();

		// If item created from class list (not from file) then launch
		//  the server to edit the item.
		if (dlg.GetSelectionType() == COleInsertDialog::createNewItem)
			pItem->DoVerb(OLEIVERB_OPEN, NULL);

		ASSERT_VALID(pItem);

	}
	CATCH(CException, e)
	{
		if (pItem != NULL)
		{
			ASSERT_VALID(pItem);
			pItem->Delete();
		}
		AfxMessageBox("Ole - Failed to Create Object");
		return NULL;
	}
	END_CATCH

	return pOleDocument;
}

extern "C" void FAR PASCAL _export CntrDLLInsertObjectDelete(void *pData)
{
	COleDocument* pDoc = (COleDocument*) pData;
	delete pDoc;
}

extern "C" HGLOBAL FAR PASCAL _export CntrDLLInsertObjectGetHGlobal(void* pData)
{
	COleDocument* pDoc = (COleDocument*) pData;
	ASSERT_VALID(pDoc);

	POSITION pos = pDoc->GetStartPosition();
	COleClientItem* pItem = pDoc->GetNextClientItem(pos);

	HGLOBAL hStorage;
	HRESULT hr;
	hr = ::GetHGlobalFromILockBytes(pItem->m_lpLockBytes, &hStorage);
	if (hr != NOERROR) {
		return NULL;
	}
	
	return hStorage;
}

extern "C" void* FAR PASCAL _export CntrDLLContainerObjectNew()
{
	CClientItem* pClientItem = new CClientItem;
	return pClientItem;
}

extern "C" void FAR PASCAL _export CntrDLLContainerObjectDelete(void* pData)
{
	CClientItem* pClientItem = (CClientItem*) pData;
	delete pClientItem;
}

extern "C" BOOL FAR PASCAL _export CntrDLLContainerObjectActivate(void* pItem, void _huge* pData, long nSize)
{
	CClientItem* pClientItem = (CClientItem*) pItem;
	TRY
	{
		pClientItem->LoadEmbedding(pData, nSize);
	}
	CATCH(CException, e)
	{
		return FALSE;
	}
	END_CATCH

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// Library init

class CCntrDLL : public CWinApp
{
public:
	virtual BOOL InitInstance(); // Initialization
	virtual int ExitInstance();  // Termination (WEP-like code)

	// nothing special for the constructor
	CCntrDLL(const char* pszAppName)
		: CWinApp(pszAppName)
		{ }
};

BOOL CCntrDLL::InitInstance()
{
	// any DLL initialization goes here
	TRACE("TRACER.DLL initializing\n");
	SetDialogBkColor();     // grey dialogs in the DLL as well
	return TRUE;
}

int CCntrDLL::ExitInstance()
{
	// any DLL termination goes here (WEP-like code)
	return CWinApp::ExitInstance();
}


extern "C" BOOL FAR PASCAL _export FilterDllMsg(LPMSG lpMsg)
{
	TRY
	{
		return AfxGetApp()->PreTranslateMessage(lpMsg);
	}
	END_TRY
	return FALSE;
}

extern "C" void FAR PASCAL _export ProcessDllIdle()
{
	TRY
	{
		// flush it all at once
		long lCount = 0;
		while (AfxGetApp()->OnIdle(lCount))
			lCount++;
	}
	END_TRY
}

CCntrDLL  NEAR appCntrDLL("Cntrdll.dll");

/////////////////////////////////////////////////////////////////////////
//
CClientItem::CClientItem()
{
	m_lpObject = NULL;
}

CClientItem::~CClientItem()
{
	HRESULT hr;
	if (m_lpObject) {
		hr = m_lpObject->Close(OLECLOSE_NOSAVE);
		m_lpObject->Release();
	}
}



/////////////////////////////////////////////////////////////////////////////
// CClientItem OLE interface implementation

BEGIN_INTERFACE_MAP(CClientItem, CCmdTarget)
	INTERFACE_PART(CClientItem, IID_IOleClientSite, OleClientSite)
END_INTERFACE_MAP()

/////////////////////////////////////////////////////////////////////////////
// Implementation of IOleClientSite

STDMETHODIMP_(ULONG) CClientItem::XOleClientSite::AddRef()
{
	METHOD_PROLOGUE(CClientItem, OleClientSite)
	return (ULONG)pThis->ExternalAddRef();
}

STDMETHODIMP_(ULONG) CClientItem::XOleClientSite::Release()
{
	METHOD_PROLOGUE(CClientItem, OleClientSite)
	return (ULONG)pThis->ExternalRelease();
}

STDMETHODIMP CClientItem::XOleClientSite::QueryInterface(
	REFIID iid, LPVOID far* ppvObj)
{
	METHOD_PROLOGUE(CClientItem, OleClientSite)
	return (HRESULT)pThis->ExternalQueryInterface(&iid, ppvObj);
}

STDMETHODIMP CClientItem::XOleClientSite::SaveObject()
{
	METHOD_PROLOGUE(CClientItem, OleClientSite)
	ASSERT_VALID(pThis);

	TRACE0("Save Object Called");
	HRESULT hr = NOERROR;
	return hr;
}

STDMETHODIMP CClientItem::XOleClientSite::GetMoniker(
	DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER FAR* ppMoniker)
{
	METHOD_PROLOGUE(CClientItem, OleClientSite)
	ASSERT_VALID(pThis);

	ASSERT(ppMoniker != NULL);
	*ppMoniker = NULL;

	return *ppMoniker != NULL ? NOERROR : ResultFromScode(E_FAIL);
}

STDMETHODIMP CClientItem::XOleClientSite::GetContainer(
	LPOLECONTAINER FAR* ppContainer)
{
	METHOD_PROLOGUE(CClientItem, OleClientSite)
	ASSERT_VALID(pThis);

	// return the IOleItemContainer interface in the document
	*ppContainer = NULL;

	return *ppContainer != NULL ? NOERROR : ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP CClientItem::XOleClientSite::ShowObject()
{
	METHOD_PROLOGUE(CClientItem, OleClientSite)
	ASSERT_VALID(pThis);

	return NOERROR;
}

STDMETHODIMP CClientItem::XOleClientSite::OnShowWindow(BOOL fShow)
{
	METHOD_PROLOGUE(CClientItem, OleClientSite)
	ASSERT_VALID(pThis);

	return ResultFromScode(E_NOTIMPL);
}

STDMETHODIMP CClientItem::XOleClientSite::RequestNewObjectLayout()
{
	return ResultFromScode(E_NOTIMPL);
}

// helper to get client site -- this is called from a number of places
LPOLECLIENTSITE CClientItem::GetClientSite()
{
	ASSERT_VALID(this);

	LPOLECLIENTSITE lpClientSite =
		(LPOLECLIENTSITE)GetInterface(&IID_IOleClientSite);
	ASSERT(lpClientSite != NULL);
	return lpClientSite;
}	  

/////////////////////////////////////////////////////////////////
// OLE1STREAM

typedef struct {
	OLESTREAM m_ole1stream;
	unsigned char _huge* m_pData;
	DWORD m_nSize;
	DWORD m_nCount;
} OLE1STREAM, *LPOLE1STREAM;

DWORD FAR PASCAL Ole1StreamGet(LPOLESTREAM pOleStream, void FAR* pVoid, DWORD cb)
{ 
	LPBYTE pBytes = (LPBYTE)pVoid;
	LPOLE1STREAM pStream = (LPOLE1STREAM)pOleStream;
	if (pStream->m_pData == NULL) {
		return 0;
	}
	DWORD nBytesLeft = pStream->m_nSize - pStream->m_nCount;
	
	DWORD nBytesToCopy = min(cb, nBytesLeft);
	if (nBytesToCopy>0xffff) {
		TRACE0("WARNING: Copying more than 64K bytes.");
	}

	// we can supply all the bytes
	hmemcpy(pBytes,pStream->m_pData+pStream->m_nCount,(long)nBytesToCopy);

	pStream->m_nCount += nBytesToCopy;
	return nBytesToCopy;
}
	
DWORD FAR PASCAL Ole1StreamPut(LPOLESTREAM pStream, const void FAR* pData, DWORD cb)
{
	// this should not be called for us
	ASSERT(FALSE);
	return cb;
}

LPOLE1STREAM Ole1StreamNew(void _huge* pData, long nSize)
{
	LPOLE1STREAM pOle1Stream = new OLE1STREAM;

	pOle1Stream->m_nSize = nSize;
	pOle1Stream->m_pData = (unsigned char _huge*)pData;
	pOle1Stream->m_ole1stream.lpstbl = new OLESTREAMVTBL;
	pOle1Stream->m_ole1stream.lpstbl->Get = Ole1StreamGet;
	pOle1Stream->m_ole1stream.lpstbl->Put = Ole1StreamPut;

	pOle1Stream->m_nCount = 0;

	return pOle1Stream;
}

void Ole1StreamDelete(LPOLE1STREAM pOle1Stream)
{
	delete pOle1Stream->m_ole1stream.lpstbl;
	delete pOle1Stream;
}

/////////////////////////////////////////////////////////////////
// code to launch OLE1EMBEDDING

// HGLOBAL must be allocated as SHARED
void CClientItem::LoadEmbedding(void _huge* pData, long nSize)
{
	ASSERT_VALID(this);
	ASSERT(m_lpObject == NULL);
	LPLOCKBYTES lpLockBytes;
	LPSTORAGE lpStorage;

	HRESULT hr;
	
	// Create a new LockBytes structure after freeing the old one
	hr = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
	if (hr != NOERROR) {
		AfxThrowOleException(hr);
	}
	ASSERT(lpLockBytes != NULL);

	// create a new LPStorage Object after freeing the old one
	hr = ::StgCreateDocfileOnILockBytes(lpLockBytes,
		STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_READWRITE, 0, &lpStorage);
	if ((SCODE)hr != S_OK)
	{
		VERIFY(lpLockBytes->Release() == 0);
		lpStorage = NULL;
		AfxThrowOleException(hr);
	}
	ASSERT(lpStorage != NULL);

	// Create an Ole1Stream
	LPOLE1STREAM pOle1Stream = Ole1StreamNew(pData, nSize);

	// convert the ole1 stream to an IStorage
	hr = ::OleConvertOLESTREAMToIStorage(&pOle1Stream->m_ole1stream,lpStorage,NULL);
	if ((SCODE)hr != S_OK)
	{
		Ole1StreamDelete(pOle1Stream);
		VERIFY(lpStorage->Release() == 0);
		VERIFY(lpLockBytes->Release() == 0);
		lpStorage = NULL;
		AfxThrowOleException(hr);
	}
	ASSERT(lpStorage != NULL);

	ASSERT_VALID(this);

	// load the embedding...
	// attempt to load the object from the storage
	hr = ::OleLoad(lpStorage, IID_IOleObject, GetClientSite(), (LPLP)&m_lpObject);
	if (hr != S_OK) {
		Ole1StreamDelete(pOle1Stream);
		VERIFY(lpStorage->Release());
		VERIFY(lpLockBytes->Release());
		AfxThrowOleException(hr);
	}

	hr = m_lpObject->DoVerb(OLEIVERB_OPEN, NULL, NULL, 0, NULL, NULL);

	Ole1StreamDelete(pOle1Stream);
	VERIFY(lpStorage->Release());
	VERIFY(lpLockBytes->Release());
	if (hr != S_OK) {
		AfxThrowOleException(hr);
	}
}

// HGLOBAL must be allocated as SHARED
// THIS ONE WORKS with an OLE2 object
void CClientItem::LoadEmbedding2(HGLOBAL hGlobal)
{
	ASSERT_VALID(this);
	ASSERT(m_lpObject == NULL);
	LPLOCKBYTES lpLockBytes;
	LPSTORAGE lpStorage;

	HRESULT hr;

	// Create a new LockBytes structure after freeing the old one
	hr = ::CreateILockBytesOnHGlobal(hGlobal, TRUE, &lpLockBytes);
	if (hr != NOERROR)
		AfxThrowOleException(hr);
	ASSERT(lpLockBytes != NULL);

	// create a new LPStorage Object after freeing the old one
	hr = ::StgOpenStorageOnILockBytes(lpLockBytes, NULL,
		STGM_SHARE_EXCLUSIVE | STGM_READWRITE,NULL, 0, &lpStorage);
	if ((SCODE)hr != S_OK)
	{
		VERIFY(lpLockBytes->Release() == 0);
		lpStorage = NULL;
		AfxThrowOleException(hr);
	}
	ASSERT(lpStorage != NULL);

	{
		LPSTREAM lpStream;
		hr = lpStorage->OpenStream("Contents",0,STGM_READWRITE|
			STGM_SHARE_EXCLUSIVE,0,&lpStream);
		char pBuffer[256];
		ULONG nCount;
		hr = lpStream->Read(pBuffer,256,&nCount);
		VERIFY(lpStream->Release() == 0);
	}

	ASSERT_VALID(this);

	// load the embedding...
	// attempt to load the object from the storage
	hr = ::OleLoad(lpStorage, IID_IOleObject, GetClientSite(), (LPLP)&m_lpObject);
	if (hr != S_OK) {
		VERIFY(lpStorage->Release());
		VERIFY(lpLockBytes->Release());
		AfxThrowOleException(hr);
	}

	hr = m_lpObject->DoVerb(OLEIVERB_OPEN, NULL, NULL, 0, NULL, NULL);

	VERIFY(lpStorage->Release());
	VERIFY(lpLockBytes->Release());
}
