// ===========================================================================
// 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 =======================================================================