// ===========================================================================
// File: S S E R V E R . C P P
// 
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// 
// Description:
// 
//  This is the server-portion of the SIMPLE Network OLE sample. This
// application implements the CLSID_SimpleObject class as a LocalServer.
// Instances of this class support a limited form of the IStream interface --
// calls to IStream::Read and IStream::Write will "succeed" (they do nothing),
// and calls on any other methods fail with E_NOTIMPL.
// 
//  The purpose of this sample is to demonstrate what is minimally required
// to implement an object that can be used by clients (both those on the same
// machine using OLE and those using Network OLE across the network).
// 
// Instructions:
// 
//  To use this sample:
//   * build it using the NMAKE command. NMAKE will create SSERVER.EXE and
//     SCLIENT.EXE.
//   * edit the SSERVER.REG file to make the LocalServer32 key point to the
//     location of SSERVER.EXE, and run the INSTALL.BAT command (it simply
//     performs REGEDIT SSERVER.REG)
//   * run SSERVER.EXE. it should display the message "Waiting..."
//   * run SCLIENT.EXE on the same machine using no command-line arguments,
//     or from another machine using the machine-name (UNC or DNS) as the sole
//     command-line argument. it will connect to the server, perform some read
//     and write calls, and disconnect. both SSERVER.EXE and SCLIENT.EXE will
//     automatically terminate. both applications will display some status text.
//   * you can also run SCLIENT.EXE from a different machine without having first
//     run SSERVER.EXE on the machine. in this case, SSERVER.EXE will be launched
//     by OLE in the background and you will be able to watch the output of
//     SCLIENT.EXE but the output of SSERVER.EXE will be hidden.
//   * to examine the automatic launch-security features of Network OLE, try
//     using the '...\CLSID\{...}\LaunchPermission = Y' key commented out in 
//     the SSERVER.REG file and reinstalling it. by setting different read-access
//     privileges on this key (using the Security/Permissions... dialog in the
//     REGEDT32 registry tool built into the system) you can allow other
//     users to run the SCLIENT.EXE program from their accounts.
// 
// Copyright 1996 Microsoft Corporation.  All Rights Reserved.
// ===========================================================================

// %%Includes: ---------------------------------------------------------------
#define INC_OLE2
#define STRICT
#include <stdio.h>
#include <windows.h>
#include <initguid.h>

// %%GUIDs: ------------------------------------------------------------------
DEFINE_GUID(CLSID_SimpleObject, 0x5e9ddec7, 0x5767, 0x11cf, 0xbe, 0xab, 0x0, 0xaa, 0x0, 0x6c, 0x36, 0x6);

// %%Globals: ----------------------------------------------------------------
HANDLE          hevtDone;

// %%Classes: ----------------------------------------------------------------
// simple class-factory: only knows how to create CSimpleObject instances
class CClassFactory : public IClassFactory {
  public:
    // IUnknown
    STDMETHODIMP    QueryInterface (REFIID riid, void** ppv);
    STDMETHODIMP_(ULONG) AddRef(void)  { return 1; };
    STDMETHODIMP_(ULONG) Release(void) { return 1; }

    // IClassFactory
    STDMETHODIMP    CreateInstance (LPUNKNOWN punkOuter, REFIID iid, void **ppv);
    STDMETHODIMP    LockServer (BOOL fLock) { return E_FAIL; };
    };

// simple object supporting a dummy IStream
class CSimpleObject : public IStream {
  public:
    // IUnknown
    STDMETHODIMP    QueryInterface (REFIID iid, void **ppv);
    STDMETHODIMP_(ULONG) AddRef(void)  { return InterlockedIncrement(&m_cRef); };
    STDMETHODIMP_(ULONG) Release(void) { if (InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; } return 1; }

    // IStream
    STDMETHODIMP    Read(void *pv, ULONG cb, ULONG *pcbRead);
    STDMETHODIMP    Write(VOID const *pv, ULONG cb, ULONG *pcbWritten);
    STDMETHODIMP    Seek(LARGE_INTEGER dbMove, DWORD dwOrigin, ULARGE_INTEGER *pbNewPosition)
        { return E_FAIL; }
    STDMETHODIMP    SetSize(ULARGE_INTEGER cbNewSize)
        { return E_FAIL; }
    STDMETHODIMP    CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
        { return E_FAIL; }
    STDMETHODIMP    Commit(DWORD grfCommitFlags)
        { return E_FAIL; }
    STDMETHODIMP    Revert(void)
        { return E_FAIL; }
    STDMETHODIMP    LockRegion(ULARGE_INTEGER bOffset, ULARGE_INTEGER cb, DWORD dwLockType)
        { return E_FAIL; }
    STDMETHODIMP    UnlockRegion(ULARGE_INTEGER bOffset, ULARGE_INTEGER cb, DWORD dwLockType)
        { return E_FAIL; }
    STDMETHODIMP    Stat(STATSTG *pstatstg, DWORD grfStatFlag)
        { return E_FAIL; }
    STDMETHODIMP    Clone(IStream **ppstm)
        { return E_FAIL; }

    // constructors/destructors
    CSimpleObject()     { m_cRef = 1; }
    ~CSimpleObject()    { SetEvent(hevtDone); }

  private:
    LONG        m_cRef;
    };

// %%Globals: ----------------------------------------------------------------
CClassFactory   g_ClassFactory;

// ---------------------------------------------------------------------------
// %%Function: Message
// 
//  Formats and displays a message to the console.
// ---------------------------------------------------------------------------
 void
Message(LPTSTR szPrefix, HRESULT hr)
{
    LPTSTR   szMessage;

    if (hr == S_OK)
        {
        wprintf(szPrefix);
        wprintf(TEXT("\n"));
        return;
        }
 
    if (HRESULT_FACILITY(hr) == FACILITY_WINDOWS)
        hr = HRESULT_CODE(hr);

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        hr,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), //The user default language
        (LPTSTR)&szMessage,
        0,
        NULL );

    wprintf(TEXT("%s: %s(%lx)\n"), szPrefix, szMessage, hr);
    
    LocalFree(szMessage);
}  // Message

// ---------------------------------------------------------------------------
// %%Function: CSimpleObject::QueryInterface
// ---------------------------------------------------------------------------
 STDMETHODIMP
CSimpleObject::QueryInterface(REFIID riid, void** ppv)
{
    if (ppv == NULL)
        return E_INVALIDARG;
    if (riid == IID_IUnknown || riid == IID_IStream)
        {
        *ppv = (IUnknown *) this;
        AddRef();
        return S_OK;
        }
    *ppv = NULL;
    return E_NOINTERFACE;
}  // CSimpleObject::QueryInterface

// ---------------------------------------------------------------------------
// %%Function: CSimpleObject::Read
// ---------------------------------------------------------------------------
 STDMETHODIMP
CSimpleObject::Read(void *pv, ULONG cb, ULONG *pcbRead)
{
    Message(TEXT("Server: IStream:Read"), S_OK);
    if (!pv && cb != 0)
        return E_INVALIDARG;

    // fill the buffer with FF's. we could read it from somewhere.
    if (cb != 0)
        memset(pv, 0xFF, cb);

    if (pcbRead)
        *pcbRead = cb;
    return S_OK;
}  // CSimpleObject::Read

// ---------------------------------------------------------------------------
// %%Function: CSimpleObject::Write
// ---------------------------------------------------------------------------
 STDMETHODIMP
CSimpleObject::Write(VOID const *pv, ULONG cb, ULONG *pcbWritten)
{
    Message(TEXT("Server: IStream:Write"), S_OK);
    if (!pv && cb != 0)
        return E_INVALIDARG;
    // ignore the data, but we could examine it or put it somewhere
    if (pcbWritten)
        *pcbWritten = cb;
    return S_OK;
}  // CSimpleObject::Write

// ---------------------------------------------------------------------------
// %%Function: CClassFactory::QueryInterface
// ---------------------------------------------------------------------------
 STDMETHODIMP
CClassFactory::QueryInterface(REFIID riid, void** ppv)
{
    if (ppv == NULL)
        return E_INVALIDARG;
    if (riid == IID_IClassFactory || riid == IID_IUnknown)
        {
        *ppv = (IClassFactory *) this;
        AddRef();
        return S_OK;
        }
    *ppv = NULL;
    return E_NOINTERFACE;
}  // CClassFactory::QueryInterface

// ---------------------------------------------------------------------------
// %%Function: CClassFactory::CreateInstance
// ---------------------------------------------------------------------------
 STDMETHODIMP
CClassFactory::CreateInstance(LPUNKNOWN punkOuter, REFIID riid, void** ppv)
{
    LPUNKNOWN   punk;
    HRESULT     hr;

    *ppv = NULL;

    if (punkOuter != NULL)
        return CLASS_E_NOAGGREGATION;

    Message(TEXT("Server: IClassFactory:CreateInstance"), S_OK);

    punk = new CSimpleObject;

    if (punk == NULL)
        return E_OUTOFMEMORY;

    hr = punk->QueryInterface(riid, ppv);
    punk->Release();
    return hr;
}  // CClassFactory::CreateInstance

// ---------------------------------------------------------------------------
// %%Function: main
// ---------------------------------------------------------------------------
 void __cdecl
main()
{
    HRESULT hr;
    DWORD   dwRegister;

    // create the thread which is signaled when the instance is deleted
    hevtDone = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (hevtDone == NULL)
        {
        hr = HRESULT_FROM_WIN32(GetLastError());
        Message(TEXT("Server: CreateEvent"), hr);
        exit(hr);
        }

    // initialize COM for free-threading
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (FAILED(hr))
        {
        Message(TEXT("Server: CoInitializeEx"), hr);
        exit(hr);
        }

    // register the class-object with OLE
    hr = CoRegisterClassObject(CLSID_SimpleObject, &g_ClassFactory,
        CLSCTX_SERVER, REGCLS_SINGLEUSE, &dwRegister);
    if (FAILED(hr))
        {
        Message(TEXT("Server: CoRegisterClassObject"), hr);
        exit(hr);
        }

    Message(TEXT("Server: Waiting"), S_OK);

    // wait until an object is created and deleted.
    WaitForSingleObject(hevtDone, INFINITE);

    CloseHandle(hevtDone);

    CoUninitialize();
    Message(TEXT("Server: Done"), S_OK);
}  // main

// EOF =======================================================================

